2017-02-16 7 views
0

Also muss ich eine Abfrage in SQL schreiben, wo ich die Prominenten auflisten, die in Beziehungen mit dem gleichen Promi gewesen sind. Ich liste hauptsächlich celeb1, celeb2 und celeb3 auf, wo celeb3 in einer Beziehung mit celeb1 und celeb2 war. Hier ist die Abfrage Ich verwende:SQL-Abfrage, wo das Ergebnis doppelte Zeilen hat

SELECT S1.Celeb1, S2.Celeb2, S3.name AS Celeb3 
FROM Relationships S1, Relationships S2, Celebs S3 
WHERE S3.name = S1.Celeb2 
AND S3.name = S2.Celeb1 
AND S1.Celeb1 <> S2.Celeb2; 

Es ist schwierig zu wissen, ob diese Abfrage korrekt ist, da es mir 200 Zeilen im Ergebnis gibt, aber ich sah ein paar der Reihe und es sieht aus wie es mir geben Korrektes Ergebnis, wenn celeb3 in einer Beziehung mit celeb1 und 2 war. Das Problem ist, dass im Ergebnis doppelte Zeilen vorhanden sind. Dies könnte darauf zurückzuführen sein, dass in der Beziehungstabelle die Beziehung celeb1, celeb2 aufgelistet wird, aber auch die inverse celeb2, celeb1, aufgelistet wird. Wie kann ich verhindern, dass das Ergebnis Duplikate auflistet?

Hier sind die zwei Tabellen, die ich verwende (Beziehungen und Promis).

CREATE TABLE Celebs(
    name VARCHAR(30) 
); 

CREATE TABLE Relationships (
    Celeb1 VARCHAR(30), 
    Celeb2 VARCHAR(30) 
); 
+2

Tipp von heute: Wechseln Sie zu moderner, expliziter 'JOIN'-Syntax. Einfacher zu schreiben (ohne Fehler), einfacher zu lesen und zu pflegen und einfacher zu Outer Join zu konvertieren, falls erforderlich. – jarlh

+0

Würde dies mir helfen, das Problem zu lösen, das ich habe? –

+0

Nein, aber bringen Sie Ihr SQL-Wissen auf die nächste Stufe! – jarlh

Antwort

0

Wenn Celeb3 in einer Beziehung mit A und B war, werden Sie auch B, A als Ergebnis erhalten. Um zu vermeiden, dass machen nur eine Einschränkung, die A> B:

SELECT DISTINCT S1.Celeb1, S2.Celeb2, S3.name AS Celeb3 
FROM Relationships S1, Relationships S2, Celebs S3 
WHERE S3.name = S1.Celeb2 
AND S3.name = S2.Celeb1 
AND S1.Celeb1 > S2.Celeb2 
+0

Das funktioniert nicht Ich bekomme immer noch doppelte Zeilen. –

+0

Fügen Sie eine 'disctinct'-Klausel ... Bearbeiten meiner Antwort –

+0

Es sieht aus wie das funktioniert, obwohl mein Ergebnis mich 598 Zeilen und meine Beziehungen Tabelle hat 425 Zeilen. Scheint die 598 Zeilen ein wenig hoch oder ist das gut? Wenn ich stattdessen A <> B mache, bekomme ich 1198 Zeilen. Warum das? –

1

die an einer Probe Werfen wir einen Blick:

 
celeb1 celeb2 
A  B 
B  C 
C  D 

Erwartetes Ergebnis:

  • A und C waren beide mit B.
  • B und D wurden sowohl mit C.

Um diese Übereinstimmungen zu finden, empfehle ich, Tupel zu duplizieren, so dass jedes Paar zweimal in der Tabelle ist (wenn das nicht schon der Fall ist).

 
celeb1 celeb2 
A  B 
B  A 
B  C 
C  B 
C  D 
D  C 

Wir können bereits sehen, dass B und C jeweils zwei Partner hatten. Verknüpfen Sie diesen Datensatz mit sich selbst, um die Datensätze zu verbinden.

with rel as 
(
    select celeb1 as cel1, celeb2 as cel2 from relationships 
    union 
    select celeb2 as cel1, celeb1 as cel2 from relationships 
) 
select rel1.cel2 as celeb1, rel2.cel2 as celeb2, rel1.cel1 as partner 
from rel rel1 
join rel rel2 on rel2.cel1 = rel1.cel1 and rel2.cel2 > rel1.cel2 
order by 1, 2, 3; 
+0

Das ist der eine. Ich hatte mit genau dem gleichen UNION-Ansatz begonnen und wollte es lokal validieren, aber mein SQL-Entwickler stürzte beim Start ab :). Gute Arbeit. –

+0

MySQL Anpassung dieses Ansatzes mit den Daten, die ich vorbereitet http://sqlfiddle.com/#!9/0933ef/5 –

0

Oracle-Setup:

CREATE TABLE celebs (name) AS 
    SELECT 'A' FROM DUAL UNION ALL 
    SELECT 'B' FROM DUAL UNION ALL 
    SELECT 'C' FROM DUAL UNION ALL 
    SELECT 'D' FROM DUAL; 

CREATE TABLE relationships (celeb1, celeb2) AS 
    SELECT 'A', 'B' FROM DUAL UNION ALL 
    SELECT 'B', 'C' FROM DUAL UNION ALL 
    SELECT 'C', 'D' FROM DUAL; 

Abfrage:

SELECT DISTINCT 
     c.name, 
     CASE c.name WHEN r.celeb1 THEN r.celeb2 ELSE r.celeb1 END AS has_relationship_with 
FROM celebs c 
     LEFT OUTER JOIN 
     relationships r 
     ON (c.name = r.celeb1 OR c.name = r.celeb2); 

Ausgang:

NAME HAS_RELATIONSHIP_WITH 
---- --------------------- 
A B 
B A 
B C 
C B 
C D 
D C 

Wenn Sie A,B wollen und wollen nicht die inverse B,A dann die für die ON Klausel ändern beitreten:

ON ( (c.name = r.celeb1 AND c.name < r.celeb2) 
    OR (c.name = r.celeb2 AND c.name < r.celeb1)) 

Abfrage 2:

Sie könnten dann diese Gruppe LISTAGG mit nur zu erhalten eine Zeile pro Person:

SELECT name, 
     LISTAGG(rel, ',') WITHIN GROUP (ORDER BY rel) AS has_relationship_with 
FROM (
    SELECT DISTINCT 
     c.name, 
     CASE c.name WHEN r.celeb1 THEN r.celeb2 ELSE r.celeb1 END AS rel 
    FROM celebs c 
     LEFT OUTER JOIN 
     relationships r 
     ON (c.name = r.celeb1 OR c.name = r.celeb2) 
) 
GROUP BY name; 

Ausgabe:

NAME HAS_RELATIONSHIP_WITH 
---- --------------------- 
A B 
B A,C 
C B,D 
D C