2009-12-11 11 views
8

Ich versuche, doppelte Zeilen basierend auf gemischten Spalten zu finden. Dies ist ein Beispiel dafür, was ich habe:Vergleichen der SQL-Tabelle mit sich selbst (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') 

Nun, wenn ich diese Abfrage ausführen:

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

ich erwarten würde beide Ids zurück zu bekommen. (1 und 2), aber ich komme immer nur die eine Zeile zurück.

Meine Gedanken wären, dass es jede Zeile vergleichen sollte, aber ich denke, das ist nicht korrekt? Um dies zu beheben, hatte ich meine Abfrage geändert werden:

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] 

Was mich beide Reihen gibt, aber die Leistung beeinträchtigt extrem schnell auf der Grundlage der Anzahl der Zeilen.

Die endgültige Lösung, die ich für für die Leistung kam und Ergebnisse war eine Gewerkschaft zu verwenden:

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] 

Aber alles in allem, ich bin fehlt offensichtlich ein Verständnis, warum dies nicht funktioniert, was bedeutet, dass ich bin wahrscheinlich etwas falsch machen. Könnte mir jemand in die richtige Richtung zeigen?

+0

die erste Abfrage mit diesen Daten Versuchen: INSERT INTO TEST (id, Test1, Test2) VALUES (1, 'C', 'B') INSERT INTO TEST (id, Test1, Test2) VALUES (2, 'B', 'C') Das sollte Ihnen beide Zeilen geben. –

Antwort

10

Nicht an einer Ungleichheit teilnehmen; Es scheint, dass die JOIN- und WHERE-Bedingungen invertiert sind.

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 

Sollte gut funktionieren.

+0

Hallo, Aus einigen Tests scheint dies immer noch langsamer als mit der Union :( Was ist der Grund für nie Ungleichheit beizutreten? Wäre die WHERE-Anweisung nicht die gleiche? (Obwohl möglicherweise Ihre Verknüpfung weniger Zeilen als die andere gibt, möglicherweise die Beschleunigung der Abfrage. Ist das der Grund?) – Kyle

+0

In meinem Test dauert die UNION-Version 3 mal so lange.Wie testen Sie genau? Der Grund nicht zu einer Ungleichheit beizutreten ist, dass der Optimierer jeden einzelnen lesen muss Diese Version kann einen Index für die Spalte test1 oder test2 oder beide verwenden.Wenn der Optimierer Ihre Abfrage nicht neu schreibt, sollten Sie eine massive Leistungsverbesserung sehen, wenn Sie diese verwenden Diese Version mit den richtigen Indizes – Aaronaught

+2

Eigentlich, jetzt, da ich darüber nachdenke, da Ihr Schema scheint keine u seful-Indizes, führt die von mir gepostete Abfrage dasselbe wie die Ungleichheits-Join-Abfrage aus; Egal, was Sie tun, Sie werden mit zwei vollständigen Clustered-Index-Scans enden, was schrecklich ist. Sie benötigen Indizes für (test1, test2) und (test2, test1), um eine bessere Leistung zu erzielen. – Aaronaught

5

Sie nur beide IDs zurück, wenn Sie sie auswählen:

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

Der Grund, dass nur eine Zeile bekommen, ist, dass nur eine Zeile (nämlich # Zeile 2) eine TEST1, die zu einer anderen Zeile der TEST2 gleich .

+1

+1, weil Sie erklärt haben * warum * die ursprüngliche Syntax nicht funktionierte. Und weil deine Antwort funktioniert. "Diese Antwort ist nützlich" –

2

Ich sehe aus wie Sie arbeiten sehr schnell in Richtung Cartiesian Join. Normalerweise Duplikate, wenn Sie schauen, um zurückzukehren, müssen Sie etwas wie laufen:

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] 

Wenn Sie die Spalten mischen, dann die benötigten Bedingungen mischen, aber so etwas wie:

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] 

Damit vergleichen Sie in jedem Join das Recht nach links und links nach rechts, sodass das WHERE nicht mehr benötigt wird.

Dieser Abfragetyp wächst jedoch exponentiell in der Ausführungszeit für jede in die Tabelle eingefügte Zeile, da Sie jede Zeile mit jeder Zeile vergleichen.

0

Dies kann ohne innere Verbindungen geschehen, wenn ich mich nicht irre. Dies ist mein erstes Mal beantwortet MySQL Frage, aber ich antworte nur, um mehr Punkte hier auf StackOverflow zu bekommen. Das Komma ist sehr wichtig, damit sich MySQL nicht beschweren kann.

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