2014-05-25 21 views
5

Ich lerne SQL durch ein GalaXQL-Tutorial.LEFT OUTER JOIN mit Unterabfrage-Syntax

Ich kann die folgende Frage (Übung 12) ausrechnen:

Generate a list of stars with star ids below 100 with columns "starname", "startemp", "planetname", and "planettemp". The list should have all stars, with the unknown data filled out with NULL. These values are, as usual, fictional. Calculate the temperature for a star with ((class+7)*intensity)*1000000, and a planet's temperature is calculated from the star's temperature minus 50 times orbit distance.

Was die Syntax ein LEFT OUTER JOIN-Abfrage zu schreiben, wenn Sie Unterabfrageelemente haben „AS“, die Sie brauchen sich zusammenschließen? Hier

ist, was ich habe:

SELECT stars.name AS starname, startemp, planets.name AS planetname, planettemp 
FROM stars, planets 
LEFT OUTER JOIN (SELECT ((stars.class + 7) * stars.intensity) * 1000000 AS startemp 
       FROM stars) 
      ON stars.starid < 100 = planets.planetid 
LEFT OUTER JOIN (SELECT (startemp - 50 * planets.orbitdistance) AS planettemp 
       FROM planets) 
      ON stars.starid < 100 

Hier ist das Datenbankschema (sorry, kann nicht die Bilddatei aufgrund der niedrigen rep post):

CREATE TABLE stars (starid INTEGER PRIMARY KEY, 
        name TEXT, 
        x DOUBLE NOT NULL, 
        y DOUBLE NOT NULL, 
        z DOUBLE NOT NULL, 
        class INTEGER NOT NULL, 
        intensity DOUBLE NOT NULL); 

CREATE TABLE hilight (starid INTEGER UNIQUE); 

CREATE TABLE planets (planetid INTEGER PRIMARY KEY, 
         starid INTEGER NOT NULL, 
         orbitdistance DOUBLE NOT NULL, 
         name TEXT, 
         color INTEGER NOT NULL, 
         radius DOUBLE NOT NULL); 

CREATE TABLE moons (moonid INTEGER PRIMARY KEY, 
        planetid INTEGER NOT NULL, 
        orbitdistance DOUBLE NOT NULL, 
        name TEXT, 
        color INTEGER NOT NULL, 
        radius DOUBLE NOT NULL); 

CREATE INDEX planets_starid ON planets (starid); 
CREATE INDEX moons_planetid ON moons (planetid); 
+0

Eigentlich würden wir bevorzugen, dass es _wasnot_ ein Bild, vor dem Text war; das macht es viel einfacher, es in Tools für die Ausführung unserer eigenen Tests zu speichern. Unter anderem scheinen Sie einen Syntaxfehler zu haben: "stars.starid <100 = planets.planetid" (wenn die db das akzeptiert, kann ich garantieren, dass Sie keine korrekten Ergebnisse erhalten). In Zukunft würden wir uns über eine bessere Beschreibung dessen im Klaren sein, was speziell nicht funktioniert und was Sie erhalten sollten ("bei diesen Startdaten sollte mein Abfrageergebnis so aussehen ..."). Dies ist besser geschrieben als einige erste Fragen, die ich gesehen habe, obwohl ... –

+0

Großartig, wird dies im Hinterkopf behalten. Vielen Dank. – verkter

Antwort

11

Lass uns das langsam aufbauen.

Erstens können über das Erhalten nur die Informationen über Sterne sehen: (! Könnte dies bekannt vorkommen sollte)

SELECT name AS starName, (class + 7) * intensity * 1000000 AS starTemp 
FROM Stars 
WHERE starId < 100 


Wir bekommen eine Liste aller Sterne, deren starId weniger als 100 (die WHERE Klausel), griff nach dem Namen und berechnete die Temperatur. An dieser Stelle brauchen wir keinen eindeutigen Bezug zur Quelle.

Als nächstes müssen wir Planeteninformationen hinzufügen. Was ist mit einem INNER JOIN (beachten Sie, dass das eigentliche Schlüsselwort INNER optional ist)?

SELECT Stars.name as starName, (Stars.class + 7) * Stars.intensity * 1000000 AS starTemp, 
     Planets.name as planetName 
FROM Stars 
INNER JOIN Planets 
     ON Planets.starId = Stars.starId 
WHERE Stars.starId < 100 

Die ON Klausel eine = verwendet (gleich) Zustand Planeten zu verbinden, um den Stern, den sie umkreisen; sonst würden wir sagen, dass sie mehr als einen Stern umkreisten, was sehr ungewöhnlich ist! Jeder Stern ist für jeden Planeten einmal aufgeführt, aber das ist zu erwarten.

... Außer jetzt haben wir ein Problem: Einige unserer Sterne von der ersten Abfrage verschwunden! Die (INNER) JOIN verursacht nur Sterne mit mindestens einem Planeten gemeldet werden. Aber wir müssen immer noch Sterne ohne Planeten melden! Was ist mit einem LEFT (OUTER) JOIN?

SELECT Stars.name as starName, (Stars.class + 7) * Stars.intensity * 1000000 AS starTemp, 
     Planets.name as planetName 
FROM Stars 
LEFT JOIN Planets 
     ON Planets.starId = Stars.starId 
WHERE Stars.starId < 100 

... Und wir haben alle Sterne zurück, mit planetNamenull zu sein (und nur einmal erscheinen), wenn es keine Planeten für diesen Stern sind. Gut soweit!

Jetzt müssen wir die Planetentemperatur hinzufügen. Sollte einfach sein:

SELECT Stars.name as starName, (Stars.class + 7) * Stars.intensity * 1000000 AS starTemp, 
     Planets.name as planetName, starTemp - (50 * Planets.orbitDistance) as planetTemp 
FROM Stars 
LEFT JOIN Planets 
     ON Planets.starId = Stars.starId 
WHERE Stars.starId < 100 

... außer, dass auf den meisten RDBMS Sie einen Syntaxfehler erhalten werden, das System unter Angabe nicht starTemp finden. Was ist los? Das Problem ist, dass der neue Spaltenalias (Name) nicht (normalerweise) verfügbar ist, bis nach der SELECT Teil der Anweisung ausgeführt wird.Was bedeutet, wir müssen wieder in die Berechnung setzen:

SELECT Stars.name as starName, (Stars.class + 7) * Stars.intensity * 1000000 AS starTemp, 
     Planets.name as planetName, 
     ((Stars.class + 7) * Stars.intensity * 1000000) - (50 * Planets.orbitDistance) as planetTemp 
FROM Stars 
LEFT JOIN Planets 
     ON Planets.starId = Stars.starId 
WHERE Stars.starId < 100 

(beachten Sie, dass die db sein kann eigentlich intelligent genug, um nur einmal die starTemp Berechnung auszuführen pro-line, aber wenn Sie schreiben es zweimal erwähnen in diesem Kontext).
Nun, das ist etwas chaotisch, aber es funktioniert. Hoffentlich werden Sie daran denken, beide Referenzen zu ändern, wenn das notwendig ist ...

Glücklicherweise können wir den Stars Teil davon in eine Unterabfrage verschieben. Wir müssen nur einmal die Berechnung für starTemp auflisten!

SELECT Stars.starName, Stars.starTemp, 
     Planets.name as planetName, 
     Stars.starTemp - (50 * Planets.orbitDistance) as planetTemp 
FROM (SELECT starId, name AS starName, (class + 7) * intensity * 1000000 AS starTemp 
     FROM Stars 
     WHERE starId < 100) Stars 
LEFT JOIN Planets 
     ON Planets.starId = Stars.starId 

Ja, das sieht aus wie ich es schreiben würde. Sollte im Wesentlichen an jedem RDBMS arbeiten.

Beachten Sie, dass die Klammer in Stars.starTemp - (50 * Planets.orbitDistance) nur für die Klarheit für den Leser da ist, würde die Bedeutung der Mathematik unverändert bleiben, wenn sie entfernt wurden. Unabhängig davon, wie gut Sie Operator-Vorrangregeln kennen, setzen Sie beim Mischen immer Klammern. Dies wird besonders vorteilhaft, wenn es sich um OR s und AND s in JOIN und WHERE Bedingungen handelt - viele Menschen verlieren den Überblick darüber, was bewirkt wird.
Beachten Sie auch, dass die implizite Joinsyntax (die durch Komma getrennte FROM -Klausel) im Allgemeinen als schlecht oder auf einigen Plattformen als absolut veraltet gilt (Abfragen werden zwar weiterhin ausgeführt, aber die DB kann Sie schimpfen). Es macht auch bestimmte Dinge - wie LEFT JOIN s - schwierig zu tun, und erhöht die Möglichkeit, sich versehentlich zu sabotieren. Also bitte, vermeide es.

+1

@ Clockwore-Muse, touche Freund. Ihre Antwort ist gründlich und gut erklärt. – SystemFun

+0

Große Erklärung. Vielen Dank für Ihre Zeit. – verkter

+0

Wenn wir also die Unterabfrage ausführen und sie dem Alias ​​zuweisen, müssen wir uns in SELECT immer noch auf table.alias beziehen. – verkter

2
WITH( 
SELECT 
    stars.name AS starname, ((star.class+7)*star.intensity)*1000000) AS startemp, 
    stars.starid 
FROM 
    stars 
) AS star_temps 
SELECT 
    planets.name AS planetname, (startemp-50*planets.orbitdistance) AS planettemp 
    star_temps.starname, star_temps.startemp 
FROM 
    star_temps LEFT OUTER JOIN planets USING (star_id) 
WHERE 
    star_temps.starid < 100; 

Alternativ kann man Konstruiere eine Unterabfrage (ich habe einen allgemeinen Tabellenausdruck verwendet), um die gleiche Aufgabe wie unten gezeigt zu lösen:

SELECT 
    planets.name AS planetname, (startemp-50*planets.orbitdistance) AS planettemp 
    star_temps.starname, star_temps.startemp 
FROM 
    (SELECT 
     stars.name AS starname, ((star.class+7)*star.intensity)*1000000) AS startemp, 
     stars.starid 
    FROM 
     stars 
) AS star_temps 
LEFT OUTER JOIN planets USING (star_id) 
WHERE 
    star_temps.starid < 100; 
+0

Danke! Können Sie dies bitte mit LEFT OUTER JOIN abschließen? – verkter

+0

Eigentlich, weil die Abfrage nicht 'LEFT JOIN' verwendet, wird es nur Zeilen drucken, die _have_ planets haben, anstatt diese Informationen auszulassen, wenn sie nicht vorhanden sind. –

+0

@ Clockwork-Muse Sie sind richtig, lesen Sie die Frage schnell zu aktualisieren, um alle Sterne enthalten. – SystemFun

6
SELECT * FROM (SELECT [...]) as Alias1 
LEFT OUTER JOIN 
    (SELECT [...]) as Alias2 
ON Alias1.id = Alias2.id 
+0

Dies ist die Syntax. : D – fieven

Verwandte Themen