2016-10-30 7 views
1

Ich habe 3 Tabellen Schüler, Einschreibungen und Klassen. Ich möchte jeden Schüler und den Abteilungscode jeder Klasse auflisten (Abteilungscode ist in Klassen) einschließlich die Schüler, die keine Klassen belegt haben (Abteilungscode wäre in diesem Fall leer). Dies ist aufgrund zukünftiger Berechnungen erforderlich, die ausgeführt werden.So kombinieren Sie natürliche Join und linken äußeren Join in SQL

Hier ist, wie ich darüber nachgedacht habe. Die folgende Abfrage ist ein Join links von Studenten und Einschreibungen, so dass ich Studenten umfassen, die in allen Klassen nicht

eingeschrieben haben
select s.B#, s.firstname, e.classid 
from students s, enrollments e 
where s.B#=e.B#(+) 

Diese Abfrage wie erwartet funktioniert.

Als nächstes werde ich Mitglied Einschreibungen und Klassen, damit ich die entsprechende dept_code für jede Einschreibung sehen:

select B#, dept_code 
from enrollments natural join classes 

Dies wird auch wie erwartet funktioniert.

Wenn ich versuche, die beiden zusammen zu bringen, obwohl ich in Probleme laufen

select s.B#, s.firstname, dept_code 
from students s, (enrollments e natural join classes) 
where s.B# = e.B#(+) 

Der Versuch, diese Abfrage auszuführen erhalte ich die Fehlermeldung:

ORA-25156: old style outer join (+) cannot be used with ANSI joins 

Kann jemand bitte erklären, was hier vor sich geht?

+1

Auch Oracle empfiehlt Syntax –

Antwort

2

Sie werden besser mit expliziten JOIN statt altem Komma getrennte Joins mit Where-Klausel sein. Das gleiche gilt für (+).

Schreiben Sie Ihre Frage, wie die statt:

select s.B#, s.firstname, dept_code 
from students s 
left join (
select * -- if there are same names of columns in both tables, specify them explicitly 
from enrollments e 
join classes c on e.? = c.? -- specify here your natural join columns 
) e on s.B# = e.B# 

Btw, sagt der Fehler, den Sie genau, dass Sie nicht im alten Stil kombinieren verbindet sich mit ANSI verbindet. Wahrscheinlich Zeit, um auf das neuere Format zu wechseln, das schon lange existiert.

+0

Beantwortung Dies ist nicht die Anforderung mit dem '(+)' äußereer Verknüpfung zu stoppen. Der innere Join wird mit dem linken Join unterbrochen. –

+0

Linker Join funktioniert nicht (siehe Kommentar zu Gordon). Sub-Abfrage ist nicht notwendig (siehe meine Antwort) –

+0

Lmao, ich bin ein bisschen müde. Ich meine nicht eine Unterabfrage, eher eine abgeleitete Tabelle :) –

0

Ihre Abfrage sollte wie folgt aussehen:

select s.B#, s.firstname, c.dept_code 
from students s left join 
    enrollments e 
    on s.B# = e.B3 left join 
    classes c 
    on e.classId = b.classId; -- I am guessing what the `JOIN` column is 

Mit anderen Worten: Sie müssen zwei left join s alle Schüler zu halten. Andernfalls könnte die Klausel on Zeilen filtern.

Ich ermutige Sie auch stark zu vermeiden und un-lernen NATURAL JOIN. Es ist nur ein Fehler, der darauf wartet, im Code zu passieren. Es stimmt nur mit Spalten überein, die auf ihren Namen basieren, und berücksichtigt dabei nicht einmal die angegebenen Fremdschlüsselbeziehungen.

Es verbirgt die Spalten, die Joins sind, was das Debugging und das Verstehen von Abfragen erheblich erschwert. Da Tabellen, die ich erstelle, fast immer Spalten wie CreatedAt und CreatedBy haben, kann sie nicht verwendet werden.

EDIT: (Für lgrade)

Sie die Bedingung in der ON Klausel setzen würde:

select s.B#, s.firstname, c.dept_code 
from students s left join 
    enrollments e 
    on s.B# = e.B3 and e.lgrade is not null left join 
    classes c 
    on e.classId = b.classId; -- I am guessing what the `JOIN` column is 
+0

In Einschreibungen gibt es auch ein lgrade-Feld (Briefklasse). Wie kann ich Ihren Ansatz so anpassen, dass nur Einschreibungen berücksichtigt werden, bei denen lgrade nicht null ist? Ich habe am Ende versucht, "wo lgrade ist nicht null" zu setzen, aber das Problem damit ist, dass es alle Schüler löscht, die wir hinzugefügt haben, die nicht in Klassen eingeschrieben sind, weil Noten für Schüler, die diese Beschreibung erfüllen, null sind. Ich möchte nur keine Zeilen in die Registrierungstabelle einfügen, in denen das Igrade auf Null gesetzt wurde. –

+0

Nicht gleich der ursprünglichen Abfrage. Der erste linke Join wird möglicherweise verglichen, während der zweite nicht. Die Eliminierung der Nullen erfordert etwas Arbeit, da der erste linke Join Duplikate von Zeilen aus der Basistabelle verursachen kann. –

1

die Klammern sind nicht wirklich nötig, Ich habe sie dort nur um die Struktur zu betonen

select  s.B#, s.firstname, c.dept_code 
from     students s 
      left join (   enrollments e 
         join  classes  c 
         on   c.classId = e.classId 
         ) 
      on s.B# = e.B3 
;    

Könnte auch so geschrieben werden (nur ein anderes Format)

select  s.B#, s.firstname, c.dept_code 
from     students s 
      left join enrollments e 
      join  classes  c 
      on   c.classId = e.classId 
      on   s.B# = e.B3 
;    

Ps. Wenn Sie Zweifel über diese Syntax haben, können Sie dies versuchen:

create table t1 (i int); 
create table t2 (i int); 
create table t3 (i int); 
create table t4 (i int); 
create table t5 (i int); 

insert into t1 (i) values (1); 
insert into t2 (i) values (1); 
insert into t3 (i) values (1); 
insert into t4 (i) values (1); 
insert into t5 (i) values (1); 

select * 
from   t1 
     join t2 
     join t3 
     join t4 
     join t5 
     on  t5.i=t4.i 
     on  t4.i=t3.i 
     on  t3.i=t2.i 
     on  t2.i=t1.i 
;  
+0

Sie müssen für jeden Join eine ON-Klausel angeben, nicht am Ende der Abfrage. Es würde einen Fehler ergeben, AFAIK –

+1

Sie werden überrascht sein. –

+0

Nun, wenn es in Oracle funktioniert, dann ist es gut zu wissen und ich täusche mich. Das würde in Postgres nicht kompilieren. –

0

ich Ihnen dringend ermutigen, zu vermeiden und un-lernen äußere verbinden, weil sie produziert Nullen. Nullen zu haben ist nur ein Fehler, der darauf wartet, im Code zu passieren. Outer Join ist überhaupt kein Join, eher eine "unnatürliche" Union, die Nullen benutzt, um Dinge zu erzwingen.

Empfohlene alternativer Ansatz: tun, um die Union selbst einen geeigneten Standardwert mit:

select B#, firstname, dept_code 
    from students 
     natural join enrollments 
     natural join classes 
union 
select B#, firstname, '{{NONE}}' as dept_code 
    from students 
where B# not in (select B# from enrollments); 
Verwandte Themen