2009-12-11 4 views
8

Sto cercando di trovare righe duplicate basate su colonne miste. Questo è un esempio di quello che ho:Confronto tabella SQL a se stesso (Self-join)

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') 

Ora, se io funzionare questa domanda:

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

mi aspetto di tornare entrambi id. (1 e 2), tuttavia, torno sempre indietro di una riga.

I miei pensieri sarebbero che dovrebbe confrontare ogni riga, ma immagino che questo non sia corretto? Per fissare questo avevo cambiato la mia domanda di essere:

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] 

che mi dà entrambe le righe, ma le prestazioni si riducono in modo estremamente rapido basato sul numero di righe.

La soluzione finale mi è venuta per per prestazioni e risultati è stato quello di utilizzare un'unione:

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] 

Ma nel complesso, Sono ovviamente manca una comprensione del perché questo non sta funzionando il che significa che sono probabilmente facendo qualcosa di sbagliato. Qualcuno potrebbe indicarmi la giusta direzione?

+0

Prova la prima query con questi dati: INSERT INTO TEST (id, test1, test2) VALORI (1, 'C', 'B') INSERT INTO TEST (id, test1, test2) VALORI (2, 'B', 'C') Questo dovrebbe fornire entrambe le righe. –

risposta

10

Non partecipare a una disuguaglianza; sembra che le condizioni JOIN e WHERE siano invertite.

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 

Dovrebbe funzionare correttamente.

+0

Ciao, Da alcuni test questo sembra ancora più lento rispetto all'utilizzo dell'unione :( Qual è la ragione per non aderire mai alla disuguaglianza? Non sarebbe l'istruzione where a essere la stessa? (Sebbene potenzialmente il tuo join restituisca meno righe dell'altro, velocizzare la query è forse la ragione?) – Kyle

+0

Nel mio test, la versione UNION impiega più di 3 volte. Come stai testando esattamente? La ragione per non aderire a una disuguaglianza è che l'ottimizzatore deve leggere ogni singolo riga che soddisfa tale condizione (ovvero quasi tutte) e filtra in seguito, questa versione può utilizzare un indice su column test1 o test2 o entrambi. A meno che l'ottimizzatore non riesca in qualche modo a riscrivere la query, si dovrebbe notare un notevole miglioramento delle prestazioni se si utilizza questa versione con gli indici appropriati – Aaronaught

+2

In realtà, ora che ci penso, dal momento che il tuo schema sembra non avere indici di seful, la query che ho postato funzionerà allo stesso modo della query di disuguaglianza-join; non importa quello che fai, finirai con due scansioni complete con indice cluster, il che è orribile. Sono necessari indici di copertura su (test1, test2) e (test2, test1) per ottenere prestazioni migliori. – Aaronaught

5

Hai solo tornare entrambi id se li si seleziona:

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

La ragione per cui ottiene solo una riga è che solo una riga (cioè riga # 2) ha una TEST1 che è uguale a TEST2 di un'altra riga .

+1

+1 perché hai spiegato * perché * la sintassi originale non funzionava. E perché la tua risposta funziona. "Questa risposta è utile" –

2

Mi sembra che tu stia lavorando molto rapidamente verso uno Cartiesian Join. Normalmente, se siete in cerca di restituire i duplicati, è necessario eseguire qualcosa di simile:

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] 

Se dovete mescolare le colonne, poi mescolare le condizioni necessarie, ma fare qualcosa di simile:

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] 

Usando questo, si confronta il diritto a sinistra e a sinistra a destra in ogni join, eliminando la necessità del WHERE del tutto.

Tuttavia, questo stile di query aumenta in modo esponenziale nel tempo di esecuzione per ogni riga inserita nella tabella, poiché si confronta ogni riga con ogni riga.

0

Questo può essere fatto senza join interni se non sbaglio. Questa è la mia prima volta che rispondo a un tipo di domanda mysql ma sto solo rispondendo per ottenere più punti qui su StackOverflow. La virgola è molto importante in modo che mysql non si lamenti.

SELECT [LEFT].[ID] FROM [TEST] AS [LEFT], [TEST] AS [RIGHT] 
WHERE [LEFT].[ID] != [RIGHT].[ID] 
AND [LEFT].[TEST1] = [RIGHT].[TEST2];