views:

550

answers:

3

I'm trying to find duplicate rows based on mixed columns. This is an example of what I have:

CREATE TABLE Test
(
   id INT PRIMARY KEY,
   test1 varchar(124),
   test2 varchar(124)
)

INSERT INTO TEST ( id, test1, test2 ) VALUES ( 1, 'A', 'B' )
INSERT INTO TEST ( id, test1, test2 ) VALUES ( 2, 'B', 'C' )

Now if I run this query:

SELECT [LEFT].[ID] 
FROM [TEST] AS [LEFT] 
   INNER JOIN [TEST] AS [RIGHT] 
   ON [LEFT].[ID] != [RIGHT].[ID] 
WHERE [LEFT].[TEST1] = [RIGHT].[TEST2]

I would expect to get back both id's. (1 and 2), however I only ever get back the one row.

My thoughts would be that it should compare each row, but I guess this is not correct? To fix this I had changed my query to be:

SELECT [LEFT].[ID] 
FROM [TEST] AS [LEFT] 
   INNER JOIN [TEST] AS [RIGHT] 
   ON [LEFT].[ID] != [RIGHT].[ID] 
WHERE [LEFT].[TEST1] = [RIGHT].[TEST2] 
OR [LEFT].[TEST2] = [RIGHT].[TEST1]

Which gives me both rows, but the performance degrades extremely quickly based on the number of rows.

The final solution I came up for for performance and results was to use a union:

SELECT [LEFT].[ID] 
FROM [TEST] AS [LEFT] 
   INNER JOIN [TEST] AS [RIGHT] 
   ON [LEFT].[ID] != [RIGHT].[ID] 
WHERE [LEFT].[TEST1] = [RIGHT].[TEST2] 
UNION
SELECT [LEFT].[ID] 
FROM [TEST] AS [LEFT] 
   INNER JOIN [TEST] AS [RIGHT] 
   ON [LEFT].[ID] != [RIGHT].[ID] 
WHERE [LEFT].[TEST2] = [RIGHT].[TEST1]

But overall, I'm obviously missing an understanding of why this is not working which means that I'm probably doing something wrong. Could someone point me in the proper direction?

+2  A: 

Dear God - never JOIN on an inequality. "Never say never," I know, but... never do that. In fact, it looks to me like you've inverted the JOIN and WHERE conditions.

SELECT t1.id
FROM Test t1
INNER JOIN Test t2
ON ((t1.test1 = t2.test2) OR (t1.test2 = t2.test1))
WHERE t1.id <> t2.id

should work fine, no?

Aaronaught
Hello,From some tests this still seems slower than using the union :(What is the reason for never joining on inequality? Wouldn't the where statement be the same? (Although potentially your join returns less rows than the other, potentially speeding up the query. Is this the reason?)
Zenox
In my test, the UNION version takes over 3 times as long. How are you testing exactly? The reason not to JOIN on an inequality is that the optimizer has to read every single row satisfying that condition (i.e. almost all of them) and filter afterward; this version can make use of an index on column test1 or test2 or both. Unless the optimizer is somehow rewriting your query, you should see a massive performance improvement if you use this version with the proper indexes.
Aaronaught
Actually, now that I think about it, since your schema appears to have no useful indexes, the query I posted will perform the same as the inequality-join query; no matter what you do, you'll end up with two full clustered-index scans, which is horrible. You need covering indexes on (test1, test2) and (test2, test1) to get any better performance.
Aaronaught
+2  A: 

You only get back both id's if you select them:

SELECT [LEFT].[ID], [RIGHT].[ID] 
FROM [TEST] AS [LEFT] 
   INNER JOIN [TEST] AS [RIGHT] 
   ON [LEFT].[ID] != [RIGHT].[ID] 
WHERE [LEFT].[TEST1] = [RIGHT].[TEST2]

The reason that only get one ROW is that only one row (namely row #2) has a TEST1 that is equal to another row's TEST2.

klausbyskov
+1 because you explained *why* the original syntax wasn't working. And because your answer works. "This answer is useful"
Ian Boyd
+1  A: 

I looks like you're working very quickly toward a Cartiesian Join. Normally if you're looking to return duplicates, you need to run something like:

SELECT [LEFT].*
FROM [TEST]  AS [LEFT]
INNER JOIN [TEST] AS [RIGHT]
    ON [LEFT].[test1] = [RIGHT].[test1]
        AND [LEFT].[test2] = [RIGHT].[test2]
        AND [LEFT].[id] <> [RIGHT].[id]

If you need to mix the columns, then mix the needed conditions, but do something like:

SELECT [LEFT].*
FROM [TEST] AS [LEFT]
INNER JOIN [TEST] AS [RIGHT]
    ON (
        [LEFT].[test1] = [RIGHT].[test2]
            OR [LEFT].[test2] = [RIGHT].[test1]
       )
        AND [LEFT].[id] <> [RIGHT].[id]

Using that, you compare the right to the left and the left to the right in each join, eliminating the need for the WHERE altogether.

However, this style of query grows exponentially in execution time for each row inserted into the table, since you're comparing each row to every row.

md5sum