it-swarm.com.de

SQL-Abfrage gibt Daten aus mehreren Tabellen zurück

Ich möchte folgendes wissen:

  • wie erhalte ich Daten aus mehreren Tabellen in meiner Datenbank?
  • welche Methoden gibt es dazu?
  • was sind Mitgliedschaften und Gewerkschaften und wie unterscheiden sie sich voneinander?
  • Wann sollte ich jeden im Vergleich zu den anderen verwenden?

Ich habe vor, dies in meiner (z. B. PHP-) Anwendung zu verwenden, möchte aber nicht mehrere Abfragen für die Datenbank ausführen. Welche Optionen habe ich, um Daten aus mehreren Tabellen in einer einzelnen Abfrage abzurufen?

Hinweis: Ich schreibe dies, da ich in der Lage sein möchte, auf eine gut geschriebene Anleitung zu den zahlreichen Fragen zu verweisen, auf die ich ständig in der Warteschlange PHP stoße, damit ich zu einem späteren Zeitpunkt darauf verweisen kann Ich poste eine Antwort.

Die Antworten decken Folgendes ab:

  1. Teil 1 - Verbindungen und Vereinigungen
  2. Teil 2 - Unterabfragen
  3. Teil 3 - Tricks und effizienter Code
  4. Teil 4 - Unterabfragen in der From-Klausel
  5. Teil 5 - Mixed Bag of John's Tricks
420
Fluffeh

Teil 1 - Mitgliedschaften und Gewerkschaften

Diese Antwort umfasst:

  1. Teil 1
    • Verbinden von zwei oder mehr Tabellen mithilfe eines Inner-Joins (Weitere Informationen finden Sie im Wikipedia-Eintrag .)
    • So verwenden Sie eine Unionsabfrage
    • Linke und rechte äußere Verknüpfungen (diese stackOverflow-Antwort eignet sich hervorragend zur Beschreibung der Verknüpfungsarten)
    • Überschneide Abfragen (und wie du sie reproduzierst, wenn deine Datenbank sie nicht unterstützt) - dies ist eine Funktion von SQL-Server ( siehe info ) und Teil des Grund, warum ich das Ganze geschrieben habe .
  2. Teil 2
    • Unterabfragen - was sie sind, wo sie verwendet werden können und worauf zu achten ist
    • Cartesian tritt der AKA bei - Oh, das Elend!

Es gibt verschiedene Möglichkeiten, Daten aus mehreren Tabellen in einer Datenbank abzurufen. In dieser Antwort verwende ich die ANSI-92-Join-Syntax. Dies unterscheidet sich möglicherweise von einer Reihe anderer Tutorials, die die ältere ANSI-89-Syntax verwenden (und wenn Sie an 89 gewöhnt sind, scheint dies viel weniger intuitiv zu sein - aber ich kann nur sagen, es auszuprobieren). viel leichter zu verstehen, wenn die Abfragen immer komplexer werden. Warum es benutzen? Gibt es einen Leistungsgewinn? Die kurze Antwort ist nein, aber es ist ​​leichter zu lesen, sobald Sie sich daran gewöhnt haben. Mit dieser Syntax ist es einfacher, Abfragen zu lesen, die von anderen Leuten geschrieben wurden.

Ich werde auch das Konzept eines kleinen Caryards verwenden, der eine Datenbank hat, um zu verfolgen, welche Autos er zur Verfügung hat. Der Eigentümer hat Sie als seinen IT-Computer-Kollegen eingestellt und erwartet, dass Sie ihm die Daten, die er verlangt, im Handumdrehen zur Verfügung stellen können.

Ich habe eine Reihe von Nachschlagetabellen erstellt, die vom Final Table verwendet werden. Dies gibt uns ein vernünftiges Modell, mit dem wir arbeiten können. Zunächst werden meine Abfragen für eine Beispieldatenbank mit der folgenden Struktur ausgeführt. Ich werde versuchen, an häufige Fehler zu denken, die am Anfang gemacht wurden, und erklären, was mit ihnen falsch läuft - und natürlich zeigen, wie man sie korrigiert.

Die erste Tabelle ist einfach eine Auflistung der Farben, damit wir wissen, welche Farben wir in der Autowerft haben.

mysql> create table colors(id int(3) not null auto_increment primary key, 
    -> color varchar(15), Paint varchar(10));
Query OK, 0 rows affected (0.01 sec)

mysql> show columns from colors;
+-------+-------------+------+-----+---------+----------------+
| Field | Type        | Null | Key | Default | Extra          |
+-------+-------------+------+-----+---------+----------------+
| id    | int(3)      | NO   | PRI | NULL    | auto_increment |
| color | varchar(15) | YES  |     | NULL    |                |
| Paint | varchar(10) | YES  |     | NULL    |                |
+-------+-------------+------+-----+---------+----------------+
3 rows in set (0.01 sec)

mysql> insert into colors (color, Paint) values ('Red', 'Metallic'), 
    -> ('Green', 'Gloss'), ('Blue', 'Metallic'), 
    -> ('White' 'Gloss'), ('Black' 'Gloss');
Query OK, 5 rows affected (0.00 sec)
Records: 5  Duplicates: 0  Warnings: 0

mysql> select * from colors;
+----+-------+----------+
| id | color | Paint    |
+----+-------+----------+
|  1 | Red   | Metallic |
|  2 | Green | Gloss    |
|  3 | Blue  | Metallic |
|  4 | White | Gloss    |
|  5 | Black | Gloss    |
+----+-------+----------+
5 rows in set (0.00 sec)

Die Markentabelle identifiziert die verschiedenen Marken der Autos, die Caryard möglicherweise verkaufen könnte.

mysql> create table brands (id int(3) not null auto_increment primary key, 
    -> brand varchar(15));
Query OK, 0 rows affected (0.01 sec)

mysql> show columns from brands;
+-------+-------------+------+-----+---------+----------------+
| Field | Type        | Null | Key | Default | Extra          |
+-------+-------------+------+-----+---------+----------------+
| id    | int(3)      | NO   | PRI | NULL    | auto_increment |
| brand | varchar(15) | YES  |     | NULL    |                |
+-------+-------------+------+-----+---------+----------------+
2 rows in set (0.01 sec)

mysql> insert into brands (brand) values ('Ford'), ('Toyota'), 
    -> ('Nissan'), ('Smart'), ('BMW');
Query OK, 5 rows affected (0.00 sec)
Records: 5  Duplicates: 0  Warnings: 0

mysql> select * from brands;
+----+--------+
| id | brand  |
+----+--------+
|  1 | Ford   |
|  2 | Toyota |
|  3 | Nissan |
|  4 | Smart  |
|  5 | BMW    |
+----+--------+
5 rows in set (0.00 sec)

Die Modelltabelle deckt verschiedene Fahrzeugtypen ab. Es wird einfacher sein, andere Fahrzeugtypen als die tatsächlichen Fahrzeugmodelle zu verwenden.

mysql> create table models (id int(3) not null auto_increment primary key, 
    -> model varchar(15));
Query OK, 0 rows affected (0.01 sec)

mysql> show columns from models;
+-------+-------------+------+-----+---------+----------------+
| Field | Type        | Null | Key | Default | Extra          |
+-------+-------------+------+-----+---------+----------------+
| id    | int(3)      | NO   | PRI | NULL    | auto_increment |
| model | varchar(15) | YES  |     | NULL    |                |
+-------+-------------+------+-----+---------+----------------+
2 rows in set (0.00 sec)

mysql> insert into models (model) values ('Sports'), ('Sedan'), ('4WD'), ('Luxury');
Query OK, 4 rows affected (0.00 sec)
Records: 4  Duplicates: 0  Warnings: 0

mysql> select * from models;
+----+--------+
| id | model  |
+----+--------+
|  1 | Sports |
|  2 | Sedan  |
|  3 | 4WD    |
|  4 | Luxury |
+----+--------+
4 rows in set (0.00 sec)

Und schließlich, um all diese anderen Tische zusammenzubinden, den Tisch, der alles zusammenhält. Das ID-Feld ist die eindeutige Chargennummer, mit der Autos identifiziert werden.

mysql> create table cars (id int(3) not null auto_increment primary key, 
    -> color int(3), brand int(3), model int(3));
Query OK, 0 rows affected (0.01 sec)

mysql> show columns from cars;
+-------+--------+------+-----+---------+----------------+
| Field | Type   | Null | Key | Default | Extra          |
+-------+--------+------+-----+---------+----------------+
| id    | int(3) | NO   | PRI | NULL    | auto_increment |
| color | int(3) | YES  |     | NULL    |                |
| brand | int(3) | YES  |     | NULL    |                |
| model | int(3) | YES  |     | NULL    |                |
+-------+--------+------+-----+---------+----------------+
4 rows in set (0.00 sec)

mysql> insert into cars (color, brand, model) values (1,2,1), (3,1,2), (5,3,1), 
    -> (4,4,2), (2,2,3), (3,5,4), (4,1,3), (2,2,1), (5,2,3), (4,5,1);
Query OK, 10 rows affected (0.00 sec)
Records: 10  Duplicates: 0  Warnings: 0

mysql> select * from cars;
+----+-------+-------+-------+
| id | color | brand | model |
+----+-------+-------+-------+
|  1 |     1 |     2 |     1 |
|  2 |     3 |     1 |     2 |
|  3 |     5 |     3 |     1 |
|  4 |     4 |     4 |     2 |
|  5 |     2 |     2 |     3 |
|  6 |     3 |     5 |     4 |
|  7 |     4 |     1 |     3 |
|  8 |     2 |     2 |     1 |
|  9 |     5 |     2 |     3 |
| 10 |     4 |     5 |     1 |
+----+-------+-------+-------+
10 rows in set (0.00 sec)

Dies wird uns genügend Daten geben (ich hoffe), um die folgenden Beispiele für verschiedene Arten von Joins abzudecken und auch genug Daten zu liefern, damit sie sich lohnen.

Der Chef will also wissen, wie es ihm geht Die IDs aller Sportwagen, die er hat.

Dies ist ein einfacher Join mit zwei Tabellen. Wir haben eine Tabelle, die das Modell und die Tabelle mit dem verfügbaren Bestand identifiziert. Wie Sie sehen, beziehen sich die Daten in der Spalte model der Tabelle cars auf die Spalte models der Tabelle cars, die wir haben. Jetzt wissen wir, dass die Modelltabelle die ID 1 für Sports hat, also schreiben wir den Join.

select
    ID,
    model
from
    cars
        join models
            on model=ID

Diese Abfrage sieht also gut aus, oder? Wir haben die beiden Tabellen identifiziert und enthalten die Informationen, die wir benötigen, und verwenden einen Join, der die zu verknüpfenden Spalten korrekt identifiziert.

ERROR 1052 (23000): Column 'ID' in field list is ambiguous

Oh nein! Ein Fehler in unserer ersten Abfrage! Ja, und es ist eine Pflaume. Sie sehen, die Abfrage enthält zwar die richtigen Spalten, einige davon sind jedoch in beiden Tabellen vorhanden, sodass die Datenbank verwirrt ist, welche Spalte wir eigentlich meinen und wo. Es gibt zwei Lösungen, um dies zu lösen. Das erste ist Nice and simple, wir können tableName.columnName verwenden, um der Datenbank genau zu sagen, was wir meinen, wie folgt:

select
    cars.ID,
    models.model
from
    cars
        join models
            on cars.model=models.ID

+----+--------+
| ID | model  |
+----+--------+
|  1 | Sports |
|  3 | Sports |
|  8 | Sports |
| 10 | Sports |
|  2 | Sedan  |
|  4 | Sedan  |
|  5 | 4WD    |
|  7 | 4WD    |
|  9 | 4WD    |
|  6 | Luxury |
+----+--------+
10 rows in set (0.00 sec)

Der andere wird wahrscheinlich häufiger verwendet und wird als Tabellen-Aliasing bezeichnet. Die Tabellen in diesem Beispiel haben nette und kurze einfache Namen, aber wenn Sie etwas wie KPI_DAILY_SALES_BY_DEPARTMENT eingeben, wird die Tabelle wahrscheinlich schnell alt.

select
    a.ID,
    b.model
from
    cars a
        join models b
            on a.model=b.ID

Nun zurück zur Anfrage. Wie Sie sehen, haben wir die Informationen, die wir benötigen, aber wir haben auch Informationen, die nicht angefordert wurden. Deshalb müssen wir eine where-Klausel in die Anweisung aufnehmen, um nur die Sportwagen zu erhalten, die angefordert wurden. Da ich die Tabellen-Alias-Methode der Verwendung der Tabellennamen vorzuziehen habe, werde ich mich ab diesem Punkt daran halten.

Natürlich müssen wir unserer Abfrage eine where-Klausel hinzufügen. Wir können Sportwagen entweder durch ID=1 oder model='Sports' identifizieren. Da die ID indiziert ist und der Primärschlüssel (und zufällig weniger tippt), können Sie dies in unserer Abfrage verwenden.

select
    a.ID,
    b.model
from
    cars a
        join models b
            on a.model=b.ID
where
    b.ID=1

+----+--------+
| ID | model  |
+----+--------+
|  1 | Sports |
|  3 | Sports |
|  8 | Sports |
| 10 | Sports |
+----+--------+
4 rows in set (0.00 sec)

Bingo! Der Chef ist glücklich. Da er ein Chef ist und mit dem, wonach er gefragt hat, nie zufrieden ist, schaut er sich die Informationen an und sagt dann Ich möchte auch die Farben.

Okay, wir haben also einen guten Teil unserer Abfrage bereits geschrieben, aber wir müssen eine dritte Tabelle verwenden, die Farben ist. In unserer Hauptinformationstabelle cars wird nun die Fahrzeugfarben-ID gespeichert und diese führt zurück zur Spalte mit der Farb-ID. Auf ähnliche Weise wie beim Original können wir uns also einem dritten Tisch anschließen:

select
    a.ID,
    b.model
from
    cars a
        join models b
            on a.model=b.ID
        join colors c
            on a.color=c.ID
where
    b.ID=1

+----+--------+
| ID | model  |
+----+--------+
|  1 | Sports |
|  3 | Sports |
|  8 | Sports |
| 10 | Sports |
+----+--------+
4 rows in set (0.00 sec)

Verdammt, obwohl die Tabelle korrekt verknüpft und die zugehörigen Spalten verknüpft wurden, haben wir vergessen, das aktuelle information aus der neuen Tabelle, die wir gerade verknüpft haben, zu übernehmen.

select
    a.ID,
    b.model,
    c.color
from
    cars a
        join models b
            on a.model=b.ID
        join colors c
            on a.color=c.ID
where
    b.ID=1

+----+--------+-------+
| ID | model  | color |
+----+--------+-------+
|  1 | Sports | Red   |
|  8 | Sports | Green |
| 10 | Sports | White |
|  3 | Sports | Black |
+----+--------+-------+
4 rows in set (0.00 sec)

Richtig, das ist der Chef, der für einen Moment von uns abweicht. Nun, um einige davon etwas detaillierter zu erklären. Wie Sie sehen, verknüpft die from-Klausel in unserer Anweisung unsere Haupttabelle (ich verwende häufig eine Tabelle, die Informationen enthält, anstatt eine Nachschlagetabelle oder Dimensionstabelle. Die Abfrage funktioniert genauso gut, wenn alle Tabellen vertauscht sind, ist jedoch weniger sinnvoll Wenn wir zu dieser Abfrage zurückkehren, um sie in ein paar Monaten zu lesen, ist es häufig am besten, eine Abfrage zu schreiben, die gut und leicht zu verstehen ist. Legen Sie sie intuitiv dar, und verwenden Sie die Option "Gut eingerückt", damit alles so klar wie möglich ist Wenn Sie andere unterrichten, versuchen Sie, diese Merkmale in ihre Abfragen zu integrieren, insbesondere, wenn Sie sie zur Fehlerbehebung verwenden.

Es ist durchaus möglich, immer mehr Tabellen auf diese Weise zu verknüpfen.

select
    a.ID,
    b.model,
    c.color
from
    cars a
        join models b
            on a.model=b.ID
        join colors c
            on a.color=c.ID
        join brands d
            on a.brand=d.ID
where
    b.ID=1

Ich habe zwar vergessen, eine Tabelle einzufügen, in der wir möglicherweise mehr als eine Spalte in der Anweisung join verknüpfen möchten, aber hier ist ein Beispiel. Wenn die Tabelle models markenspezifische Modelle und daher auch eine Spalte mit dem Namen brand enthält, die mit der Tabelle brands im Feld ID verknüpft ist, kann dies folgendermaßen erfolgen:

select
    a.ID,
    b.model,
    c.color
from
    cars a
        join models b
            on a.model=b.ID
        join colors c
            on a.color=c.ID
        join brands d
            on a.brand=d.ID
            and b.brand=d.ID
where
    b.ID=1

Wie Sie sehen, verknüpft die obige Abfrage nicht nur die verknüpften Tabellen mit der Haupttabelle cars, sondern gibt auch Verknüpfungen zwischen den bereits verknüpften Tabellen an. Wenn dies nicht getan wurde, wird das Ergebnis als kartesischer Join bezeichnet - was für schlecht zu sprechen ist. Eine kartesische Verknüpfung ist eine Verknüpfung, bei der Zeilen zurückgegeben werden, da die Informationen der Datenbank nicht mitteilen, wie die Ergebnisse begrenzt werden sollen. Die Abfrage gibt also all die Zeilen zurück, die den Kriterien entsprechen.

Um ein Beispiel für einen kartesischen Join zu geben, führen Sie die folgende Abfrage aus:

select
    a.ID,
    b.model
from
    cars a
        join models b

+----+--------+
| ID | model  |
+----+--------+
|  1 | Sports |
|  1 | Sedan  |
|  1 | 4WD    |
|  1 | Luxury |
|  2 | Sports |
|  2 | Sedan  |
|  2 | 4WD    |
|  2 | Luxury |
|  3 | Sports |
|  3 | Sedan  |
|  3 | 4WD    |
|  3 | Luxury |
|  4 | Sports |
|  4 | Sedan  |
|  4 | 4WD    |
|  4 | Luxury |
|  5 | Sports |
|  5 | Sedan  |
|  5 | 4WD    |
|  5 | Luxury |
|  6 | Sports |
|  6 | Sedan  |
|  6 | 4WD    |
|  6 | Luxury |
|  7 | Sports |
|  7 | Sedan  |
|  7 | 4WD    |
|  7 | Luxury |
|  8 | Sports |
|  8 | Sedan  |
|  8 | 4WD    |
|  8 | Luxury |
|  9 | Sports |
|  9 | Sedan  |
|  9 | 4WD    |
|  9 | Luxury |
| 10 | Sports |
| 10 | Sedan  |
| 10 | 4WD    |
| 10 | Luxury |
+----+--------+
40 rows in set (0.00 sec)

Guter Gott, das ist hässlich. In Bezug auf die Datenbank ist es jedoch gena, wonach gefragt wurde. In der Abfrage haben wir nach dem ID von cars und dem model von models gefragt. Da wir jedoch wie zum Verknüpfen der Tabellen nicht angegeben haben, hat die Datenbank jede Zeile aus der ersten Tabelle mit jede Zeile aus der Tabelle abgeglichen zweiter Tisch.

Okay, der Chef ist zurück und möchte wieder mehr Informationen. Ich möchte die gleiche Liste, füge aber auch 4WDs hinz.

Dies gibt uns jedoch eine gute Ausrede, zwei verschiedene Möglichkeiten zu prüfen, um dies zu erreichen. Wir könnten der where-Klausel eine weitere Bedingung hinzufügen:

select
    a.ID,
    b.model,
    c.color
from
    cars a
        join models b
            on a.model=b.ID
        join colors c
            on a.color=c.ID
        join brands d
            on a.brand=d.ID
where
    b.ID=1
    or b.ID=3

Das oben Genannte funktioniert zwar einwandfrei, lässt sich aber anders betrachten. Dies ist eine gute Ausrede, um zu zeigen, wie eine union-Abfrage funktioniert.

Wir wissen, dass Folgendes alle Sportwagen zurückgibt:

select
    a.ID,
    b.model,
    c.color
from
    cars a
        join models b
            on a.model=b.ID
        join colors c
            on a.color=c.ID
        join brands d
            on a.brand=d.ID
where
    b.ID=1

Und das Folgende würde alle 4WDs zurückgeben:

select
    a.ID,
    b.model,
    c.color
from
    cars a
        join models b
            on a.model=b.ID
        join colors c
            on a.color=c.ID
        join brands d
            on a.brand=d.ID
where
    b.ID=3

Wenn Sie also eine union all -Klausel dazwischen einfügen, werden die Ergebnisse der zweiten Abfrage an die Ergebnisse der ersten Abfrage angehängt.

select
    a.ID,
    b.model,
    c.color
from
    cars a
        join models b
            on a.model=b.ID
        join colors c
            on a.color=c.ID
        join brands d
            on a.brand=d.ID
where
    b.ID=1
union all
select
    a.ID,
    b.model,
    c.color
from
    cars a
        join models b
            on a.model=b.ID
        join colors c
            on a.color=c.ID
        join brands d
            on a.brand=d.ID
where
    b.ID=3

+----+--------+-------+
| ID | model  | color |
+----+--------+-------+
|  1 | Sports | Red   |
|  8 | Sports | Green |
| 10 | Sports | White |
|  3 | Sports | Black |
|  5 | 4WD    | Green |
|  7 | 4WD    | White |
|  9 | 4WD    | Black |
+----+--------+-------+
7 rows in set (0.00 sec)

Wie Sie sehen, werden zuerst die Ergebnisse der ersten Abfrage und anschließend die Ergebnisse der zweiten Abfrage zurückgegeben.

In diesem Beispiel wäre es natürlich viel einfacher gewesen, einfach die erste Abfrage zu verwenden, aber union-Abfragen können in bestimmten Fällen hilfreich sein. Sie sind eine großartige Möglichkeit, bestimmte Ergebnisse von Tabellen aus Tabellen zurückzugeben, die nicht einfach miteinander verbunden werden können - oder aus diesem Grund vollständig nicht miteinander verbundene Tabellen. Es gibt jedoch ein paar Regeln zu befolgen.

  • Die Spaltentypen aus der ersten Abfrage müssen mit den Spaltentypen aus jeder anderen der folgenden Abfragen übereinstimmen.
  • Die Namen der Spalten aus der ersten Abfrage werden verwendet, um die gesamte Ergebnismenge zu identifizieren.
  • Die Anzahl der Spalten in jeder Abfrage muss gleich sein.

Jetzt fragen Sie sich vielleicht , worin der Unterschied zwischen der Verwendung von union und union all besteht. Bei einer union-Abfrage werden Duplikate entfernt, bei einer union all nicht. Dies bedeutet, dass es einen kleinen Leistungseinbruch gibt, wenn union über union all verwendet wird, aber die Ergebnisse können sich lohnen - darüber werde ich hier allerdings nicht spekulieren.

In diesem Zusammenhang ist es möglicherweise sinnvoll, hier einige zusätzliche Anmerkungen zu machen.

  • Wenn wir die Ergebnisse bestellen wollten, können wir einen order by verwenden, aber Sie können den Alias ​​nicht mehr verwenden. In der obigen Abfrage würde das Anhängen eines order by a.ID zu einem Fehler führen. In Bezug auf die Ergebnisse heißt die Spalte ID und nicht a.ID, obwohl in beiden Abfragen derselbe Alias ​​verwendet wurde .
  • Wir können nur eine order by -Anweisung haben, und diese muss die letzte sein.

Für die nächsten Beispiele füge ich unseren Tabellen einige zusätzliche Zeilen hinzu.

Ich habe Holden zur Markentabelle hinzugefügt. Ich habe auch eine Zeile in cars hinzugefügt, die den Wert color von 12 hat - der in der Farbtabelle keinen Verweis enthält.

Okay, der Chef ist wieder da und bellt auf - * Ich möchte, dass jede Marke, die wir führen, und die Anzahl der Autos darin gezählt werden! `- Typischerweise kommen wir nur zu einem interessanten Abschnitt unserer Diskussion, und der Chef möchte mehr Arbeit .

Rightyo, also müssen wir als Erstes eine vollständige Auflistung der möglichen Marken erstellen.

select
    a.brand
from
    brands a

+--------+
| brand  |
+--------+
| Ford   |
| Toyota |
| Nissan |
| Smart  |
| BMW    |
| Holden |
+--------+
6 rows in set (0.00 sec)

Wenn wir dies nun zu unserer Autotabelle hinzufügen, erhalten wir das folgende Ergebnis:

select
    a.brand
from
    brands a
        join cars b
            on a.ID=b.brand
group by
    a.brand

+--------+
| brand  |
+--------+
| BMW    |
| Ford   |
| Nissan |
| Smart  |
| Toyota |
+--------+
5 rows in set (0.00 sec)

Was natürlich ein Problem ist - wir sehen keine Erwähnung der schönen Marke Holden, die ich hinzugefügt habe.

Dies liegt daran, dass ein Join nach übereinstimmenden Zeilen in both -Tabellen sucht. Da in Fahrzeugen vom Typ Holden keine Daten vorhanden sind, werden diese nicht zurückgegeben. Hier können wir einen outer-Join verwenden. Dies gibt all die Ergebnisse einer Tabelle zurück, unabhängig davon, ob sie in der anderen Tabelle übereinstimmen oder nicht:

select
    a.brand
from
    brands a
        left outer join cars b
            on a.ID=b.brand
group by
    a.brand

+--------+
| brand  |
+--------+
| BMW    |
| Ford   |
| Holden |
| Nissan |
| Smart  |
| Toyota |
+--------+
6 rows in set (0.00 sec)

Nun, da wir das haben, können wir eine schöne Aggregatfunktion hinzufügen, um eine Zählung zu erhalten und den Chef für einen Moment von unserem Rücken zu lassen.

select
    a.brand,
    count(b.id) as countOfBrand
from
    brands a
        left outer join cars b
            on a.ID=b.brand
group by
    a.brand

+--------+--------------+
| brand  | countOfBrand |
+--------+--------------+
| BMW    |            2 |
| Ford   |            2 |
| Holden |            0 |
| Nissan |            1 |
| Smart  |            1 |
| Toyota |            5 |
+--------+--------------+
6 rows in set (0.00 sec)

Und damit verschwindet der Chef.

Um dies näher zu erläutern, können äußere Verknüpfungen vom Typ left oder right sein. Links oder rechts definiert, welche Tabelle vollständig enthalten ist. Ein left outer join wird alle Zeilen aus der Tabelle auf der linken Seite enthalten, während (Sie haben es erraten) ein right outer join alle Ergebnisse aus der Tabelle auf der rechten Seite in die Ergebnisse bringt.

Einige Datenbanken erlauben einen full outer join, der Ergebnisse aus both -Tabellen liefert (egal ob diese übereinstimmen oder nicht), dies wird jedoch nicht in allen Datenbanken unterstützt.

Wahrscheinlich überlegen Sie sich zu diesem Zeitpunkt, ob Sie Join-Typen in einer Abfrage zusammenführen können oder nicht - und die Antwort lautet: Ja, das können Sie auf jeden Fall.

select
    b.brand,
    c.color,
    count(a.id) as countOfBrand
from
    cars a
        right outer join brands b
            on b.ID=a.brand
        join colors c
            on a.color=c.ID
group by
    a.brand,
    c.color

+--------+-------+--------------+
| brand  | color | countOfBrand |
+--------+-------+--------------+
| Ford   | Blue  |            1 |
| Ford   | White |            1 |
| Toyota | Black |            1 |
| Toyota | Green |            2 |
| Toyota | Red   |            1 |
| Nissan | Black |            1 |
| Smart  | White |            1 |
| BMW    | Blue  |            1 |
| BMW    | White |            1 |
+--------+-------+--------------+
9 rows in set (0.00 sec)

Warum sind das nicht die erwarteten Ergebnisse? Dies liegt daran, dass wir zwar die äußere Verknüpfung von Autos zu Marken ausgewählt haben, diese jedoch in der Verknüpfung zu Farben nicht angegeben haben. Daher werden bei einer bestimmten Verknüpfung nur Ergebnisse zurückgegeben, die in beiden Tabellen übereinstimmen.

Hier ist die Abfrage, die funktionieren würde, um die erwarteten Ergebnisse zu erhalten:

select
    a.brand,
    c.color,
    count(b.id) as countOfBrand
from
    brands a
        left outer join cars b
            on a.ID=b.brand
        left outer join colors c
            on b.color=c.ID
group by
    a.brand,
    c.color

+--------+-------+--------------+
| brand  | color | countOfBrand |
+--------+-------+--------------+
| BMW    | Blue  |            1 |
| BMW    | White |            1 |
| Ford   | Blue  |            1 |
| Ford   | White |            1 |
| Holden | NULL  |            0 |
| Nissan | Black |            1 |
| Smart  | White |            1 |
| Toyota | NULL  |            1 |
| Toyota | Black |            1 |
| Toyota | Green |            2 |
| Toyota | Red   |            1 |
+--------+-------+--------------+
11 rows in set (0.00 sec)

Wie wir sehen können, haben wir zwei äußere Verknüpfungen in der Abfrage und die Ergebnisse kommen wie erwartet durch.

Wie wäre es nun mit den anderen Arten von Joins, die Sie fragen? Was ist mit Kreuzungen?

Nun, nicht alle Datenbanken unterstützen intersection, aber so ziemlich alle Datenbanken ermöglichen es Ihnen, eine Schnittmenge durch einen Join (oder zumindest eine gut strukturierte where-Anweisung) zu erstellen.

Eine Schnittmenge ist eine Art von Verknüpfung, die der oben beschriebenen union ähnelt. Der Unterschied besteht jedoch darin, dass nur Datenzeilen zurückgibt, die zwischen den einzelnen verknüpften Abfragen identisch (und ich meine identisch) sind von der Gewerkschaft. Es werden nur Zeilen zurückgegeben, die in jeder Hinsicht identisch sind.

Ein einfaches Beispiel wäre als solches:

select
    *
from
    colors
where
    ID>2
intersect
select
    *
from
    colors
where
    id<4

Während eine normale union-Abfrage alle Zeilen der Tabelle zurückliefern würde (die erste Abfrage gibt etwas über ID>2 und die zweite alles über ID<4 zurück), was zu einer vollständigen Menge führen würde, würde eine Überschneidungsabfrage nur zurückliefern Die Zeile stimmt mit id=3 überein, da beide Kriterien erfüllt sind.

Wenn Ihre Datenbank jetzt keine intersect-Abfrage unterstützt, kann die obige Abfrage problemlos mit der folgenden Abfrage ergänzt werden:

select
    a.ID,
    a.color,
    a.Paint
from
    colors a
        join colors b
            on a.ID=b.ID
where
    a.ID>2
    and b.ID<4

+----+-------+----------+
| ID | color | Paint    |
+----+-------+----------+
|  3 | Blue  | Metallic |
+----+-------+----------+
1 row in set (0.00 sec)

Wenn Sie eine Schnittmenge zwischen zwei verschiedenen Tabellen mit einer Datenbank ausführen möchten, die eine Schnittmengenabfrage nicht unterstützt, müssen Sie einen Join für jede Spalte der Tabellen erstellen.

451
Fluffeh

Ok, ich fand diesen Beitrag sehr interessant und möchte einige meiner Kenntnisse über das Erstellen einer Abfrage teilen. Danke dafür Fluffeh. Anderen, die dies lesen und das Gefühl haben, falsch zu liegen, steht es 101% frei, meine Antwort zu bearbeiten und zu kritisieren. ( Ehrlich gesagt, ich bin sehr dankbar, dass ich meine Fehler korrigiert habe. )

Ich werde einige der häufig gestellten Fragen in MySQL posten.


Trick Nr. 1 ( Zeilen, die mehreren Bedingungen entsprechen )

Angesichts dieses Schemas

CREATE TABLE MovieList
(
    ID INT,
    MovieName VARCHAR(25),
    CONSTRAINT ml_pk PRIMARY KEY (ID),
    CONSTRAINT ml_uq UNIQUE (MovieName)
);

INSERT INTO MovieList VALUES (1, 'American Pie');
INSERT INTO MovieList VALUES (2, 'The Notebook');
INSERT INTO MovieList VALUES (3, 'Discovery Channel: Africa');
INSERT INTO MovieList VALUES (4, 'Mr. Bean');
INSERT INTO MovieList VALUES (5, 'Expendables 2');

CREATE TABLE CategoryList
(
    MovieID INT,
    CategoryName VARCHAR(25),
    CONSTRAINT cl_uq UNIQUE(MovieID, CategoryName),
    CONSTRAINT cl_fk FOREIGN KEY (MovieID) REFERENCES MovieList(ID)
);

INSERT INTO CategoryList VALUES (1, 'Comedy');
INSERT INTO CategoryList VALUES (1, 'Romance');
INSERT INTO CategoryList VALUES (2, 'Romance');
INSERT INTO CategoryList VALUES (2, 'Drama');
INSERT INTO CategoryList VALUES (3, 'Documentary');
INSERT INTO CategoryList VALUES (4, 'Comedy');
INSERT INTO CategoryList VALUES (5, 'Comedy');
INSERT INTO CategoryList VALUES (5, 'Action');

FRAGE

Finde alle Filme , die mindestens beiden Kategorien Comedy und Romance angehören.

Lösung

Diese Frage kann manchmal sehr knifflig sein. Es scheint, dass eine solche Frage die Antwort sein wird:

SELECT  DISTINCT a.MovieName
FROM    MovieList a
        INNER JOIN CategoryList b
            ON a.ID = b.MovieID
WHERE   b.CategoryName = 'Comedy' AND
        b.CategoryName = 'Romance'

SQLFiddle Demo

das ist definitiv sehr falsch, weil es kein Ergebnis liefert . Die Erklärung hierfür ist, dass es in jeder Zeile nur einen gültigen Wert von CategoryName gibt. Beispielsweise gibt die erste Bedingung true zurück, die zweite Bedingung ist immer false. Wenn Sie also den Operator AND verwenden, sollten beide Bedingungen erfüllt sein. Andernfalls ist es falsch. Eine andere Abfrage ist wie folgt,

SELECT  DISTINCT a.MovieName
FROM    MovieList a
        INNER JOIN CategoryList b
            ON a.ID = b.MovieID
WHERE   b.CategoryName IN ('Comedy','Romance')

SQLFiddle Demo

und das Ergebnis ist immer noch falsch, weil es mit einer Aufzeichnung übereinstimmt, die mindestens eine Übereinstimmung auf dem categoryName hat. Die echte Lösung wäre die Zählung der Anzahl der Aufnahmeinstanzen pro Film . Die Nummer der Instanz sollte mit der Gesamtzahl der in der Bedingung angegebenen Werte übereinstimmen.

SELECT  a.MovieName
FROM    MovieList a
        INNER JOIN CategoryList b
            ON a.ID = b.MovieID
WHERE   b.CategoryName IN ('Comedy','Romance')
GROUP BY a.MovieName
HAVING COUNT(*) = 2

SQLFiddle Demo (die Antwort)


Trick Nr. 2 ( Maximaler Datensatz für jeden Eintrag )

Gegebenes Schema,

CREATE TABLE Software
(
    ID INT,
    SoftwareName VARCHAR(25),
    Descriptions VARCHAR(150),
    CONSTRAINT sw_pk PRIMARY KEY (ID),
    CONSTRAINT sw_uq UNIQUE (SoftwareName)  
);

INSERT INTO Software VALUES (1,'PaintMe','used for photo editing');
INSERT INTO Software VALUES (2,'World Map','contains map of different places of the world');
INSERT INTO Software VALUES (3,'Dictionary','contains description, synonym, antonym of the words');

CREATE TABLE VersionList
(
    SoftwareID INT,
    VersionNo INT,
    DateReleased DATE,
    CONSTRAINT sw_uq UNIQUE (SoftwareID, VersionNo),
    CONSTRAINT sw_fk FOREIGN KEY (SOftwareID) REFERENCES Software(ID)
);

INSERT INTO VersionList VALUES (3, 2, '2009-12-01');
INSERT INTO VersionList VALUES (3, 1, '2009-11-01');
INSERT INTO VersionList VALUES (3, 3, '2010-01-01');
INSERT INTO VersionList VALUES (2, 2, '2010-12-01');
INSERT INTO VersionList VALUES (2, 1, '2009-12-01');
INSERT INTO VersionList VALUES (1, 3, '2011-12-01');
INSERT INTO VersionList VALUES (1, 2, '2010-12-01');
INSERT INTO VersionList VALUES (1, 1, '2009-12-01');
INSERT INTO VersionList VALUES (1, 4, '2012-12-01');

FRAGE

Suchen Sie die neueste Version für jede Software. Zeigen Sie die folgenden Spalten an: SoftwareName, Descriptions, LatestVersion ( aus Spalte VersionNo ), DateReleased

Lösung

Einige SQL-Entwickler verwenden fälschlicherweise die Aggregatfunktion MAX(). Sie neigen dazu, so zu schaffen,

SELECT  a.SoftwareName, a.Descriptions,
        MAX(b.VersionNo) AS LatestVersion, b.DateReleased
FROM    Software a
        INNER JOIN VersionList b
            ON a.ID = b.SoftwareID
GROUP BY a.ID
ORDER BY a.ID

SQLFiddle Demo

( Die meisten RDBMS erzeugen hier einen Syntaxfehler, da einige der nicht aggregierten Spalten in der group by -Klausel nicht angegeben wurden.) Das Ergebnis erzeugt die richtige LatestVersion für jede Software aber offensichtlich sind die DateReleased falsch. MySQL unterstützt Window Functions und Common Table Expression noch nicht, wie es einige RDBMS bereits tun. Die Problemumgehung für dieses Problem besteht darin, ein subquery zu erstellen, das das individuelle Maximum von versionNo für jede Software erhält und später für die anderen Tabellen verknüpft wird.

SELECT  a.SoftwareName, a.Descriptions,
        b.LatestVersion, c.DateReleased
FROM    Software a
        INNER JOIN
        (
            SELECT  SoftwareID, MAX(VersionNO) LatestVersion
            FROM    VersionList
            GROUP BY SoftwareID
        ) b ON a.ID = b.SoftwareID
        INNER JOIN VersionList c
            ON  c.SoftwareID = b.SoftwareID AND
                c.VersionNO = b.LatestVersion
GROUP BY a.ID
ORDER BY a.ID

SQLFiddle Demo (die Antwort)


Also das war es. Ich werde bald eine weitere Nachricht veröffentlichen, sobald ich eine andere FAQ auf dem Tag MySQL zurückrufe. Vielen Dank für das Lesen dieses kleinen Artikels. Ich hoffe, dass Sie zumindest ein wenig davon erfahren.

UPDATE 1


Trick Nr. 3 ( Suchen des letzten Datensatzes zwischen zwei IDs )

Gegebenes Schema

CREATE TABLE userList
(
    ID INT,
    NAME VARCHAR(20),
    CONSTRAINT us_pk PRIMARY KEY (ID),
    CONSTRAINT us_uq UNIQUE (NAME)  
);

INSERT INTO userList VALUES (1, 'Fluffeh');
INSERT INTO userList VALUES (2, 'John Woo');
INSERT INTO userList VALUES (3, 'hims056');

CREATE TABLE CONVERSATION
(
    ID INT,
    FROM_ID INT,
    TO_ID INT,
    MESSAGE VARCHAR(250),
    DeliveryDate DATE
);

INSERT INTO CONVERSATION VALUES (1, 1, 2, 'hi john', '2012-01-01');
INSERT INTO CONVERSATION VALUES (2, 2, 1, 'hello fluff', '2012-01-02');
INSERT INTO CONVERSATION VALUES (3, 1, 3, 'hey hims', '2012-01-03');
INSERT INTO CONVERSATION VALUES (4, 1, 3, 'please reply', '2012-01-04');
INSERT INTO CONVERSATION VALUES (5, 3, 1, 'how are you?', '2012-01-05');
INSERT INTO CONVERSATION VALUES (6, 3, 2, 'sample message!', '2012-01-05');

FRAGE

Finden Sie die neueste Konversation zwischen zwei Benutzern.

Lösung

SELECT    b.Name SenderName,
          c.Name RecipientName,
          a.Message,
          a.DeliveryDate
FROM      Conversation a
          INNER JOIN userList b
            ON a.From_ID = b.ID
          INNER JOIN userList c
            ON a.To_ID = c.ID
WHERE     (LEAST(a.FROM_ID, a.TO_ID), GREATEST(a.FROM_ID, a.TO_ID), DeliveryDate)
IN
(
    SELECT  LEAST(FROM_ID, TO_ID) minFROM,
            GREATEST(FROM_ID, TO_ID) maxTo,
            MAX(DeliveryDate) maxDate
    FROM    Conversation
    GROUP BY minFROM, maxTo
)

SQLFiddle Demo

98
John Woo

Teil 2 - Unterabfragen

Okay, jetzt ist der Boss wieder hereingebrochen - Ich möchte eine Liste aller unserer Autos mit der Marke und insgesamt wie viele von dieser Marke wir haben!

Dies ist eine großartige Gelegenheit, den nächsten Trick in unserer Tasche mit SQL-Goodies zu verwenden - die Unterabfrage. Wenn Sie mit dem Begriff nicht vertraut sind, ist eine Unterabfrage eine Abfrage, die in einer anderen Abfrage ausgeführt wird. Es gibt viele verschiedene Möglichkeiten, sie zu verwenden.

Stellen wir für unsere Anfrage zunächst eine einfache Abfrage zusammen, in der jedes Auto und die Marke aufgelistet sind:

select
    a.ID,
    b.brand
from
    cars a
        join brands b
            on a.brand=b.ID

Wenn wir nur die Anzahl der Autos nach Marken sortieren möchten, können wir natürlich Folgendes schreiben:

select
    b.brand,
    count(a.ID) as countCars
from
    cars a
        join brands b
            on a.brand=b.ID
group by
    b.brand

+--------+-----------+
| brand  | countCars |
+--------+-----------+
| BMW    |         2 |
| Ford   |         2 |
| Nissan |         1 |
| Smart  |         1 |
| Toyota |         5 |
+--------+-----------+

Wir sollten also in der Lage sein, einfach die Zählfunktion zu unserer ursprünglichen Abfrage hinzuzufügen, oder?

select
    a.ID,
    b.brand,
    count(a.ID) as countCars
from
    cars a
        join brands b
            on a.brand=b.ID
group by
    a.ID,
    b.brand

+----+--------+-----------+
| ID | brand  | countCars |
+----+--------+-----------+
|  1 | Toyota |         1 |
|  2 | Ford   |         1 |
|  3 | Nissan |         1 |
|  4 | Smart  |         1 |
|  5 | Toyota |         1 |
|  6 | BMW    |         1 |
|  7 | Ford   |         1 |
|  8 | Toyota |         1 |
|  9 | Toyota |         1 |
| 10 | BMW    |         1 |
| 11 | Toyota |         1 |
+----+--------+-----------+
11 rows in set (0.00 sec)

Leider können wir das nicht tun. Der Grund ist, dass wir die Auto-ID (Spalte a.ID) der Gruppe hinzufügen müssen, indem wir sie hinzufügen. Wenn also die Zählfunktion funktioniert, gibt es nur eine übereinstimmende ID pro ID.

Hier können wir jedoch eine Unterabfrage verwenden. Tatsächlich können wir zwei völlig unterschiedliche Arten von Unterabfragen ausführen, die die gleichen Ergebnisse liefern, die wir dafür benötigen. Die erste besteht darin, die Unterabfrage einfach in die select -Klausel einzufügen. Dies bedeutet, dass jedes Mal, wenn wir eine Datenzeile abrufen, die Unterabfrage ausgeführt wird, eine Datenspalte abruft und diese dann in unsere Datenzeile einfügt.

select
    a.ID,
    b.brand,
    (
    select
        count(c.ID)
    from
        cars c
    where
        a.brand=c.brand
    ) as countCars
from
    cars a
        join brands b
            on a.brand=b.ID

+----+--------+-----------+
| ID | brand  | countCars |
+----+--------+-----------+
|  2 | Ford   |         2 |
|  7 | Ford   |         2 |
|  1 | Toyota |         5 |
|  5 | Toyota |         5 |
|  8 | Toyota |         5 |
|  9 | Toyota |         5 |
| 11 | Toyota |         5 |
|  3 | Nissan |         1 |
|  4 | Smart  |         1 |
|  6 | BMW    |         2 |
| 10 | BMW    |         2 |
+----+--------+-----------+
11 rows in set (0.00 sec)

Und Bam !, das würde uns tun. Wenn Sie jedoch bemerkt haben, muss diese Unterabfrage für jede einzelne Datenzeile ausgeführt werden, die wir zurückgeben. Selbst in diesem kleinen Beispiel haben wir nur fünf verschiedene Automarken, aber die Unterabfrage wurde elf Mal ausgeführt, da wir elf Datenzeilen haben, die wir zurückgeben. In diesem Fall scheint es also nicht die effizienteste Methode zu sein, Code zu schreiben.

Lassen Sie uns für einen anderen Ansatz eine Unterabfrage ausführen und so tun, als ob es sich um eine Tabelle handelt:

select
    a.ID,
    b.brand,
    d.countCars
from
    cars a
        join brands b
            on a.brand=b.ID
        join
            (
            select
                c.brand,
                count(c.ID) as countCars
            from
                cars c
            group by
                c.brand
            ) d
            on a.brand=d.brand

+----+--------+-----------+
| ID | brand  | countCars |
+----+--------+-----------+
|  1 | Toyota |         5 |
|  2 | Ford   |         2 |
|  3 | Nissan |         1 |
|  4 | Smart  |         1 |
|  5 | Toyota |         5 |
|  6 | BMW    |         2 |
|  7 | Ford   |         2 |
|  8 | Toyota |         5 |
|  9 | Toyota |         5 |
| 10 | BMW    |         2 |
| 11 | Toyota |         5 |
+----+--------+-----------+
11 rows in set (0.00 sec)

Okay, wir haben die gleichen Ergebnisse (etwas anders sortiert - es scheint, dass die Datenbank die Ergebnisse zurückgeben wollte, die nach der ersten Spalte sortiert sind, die wir dieses Mal ausgewählt haben), aber die gleichen richtigen Zahlen.

Also, was ist der Unterschied zwischen den beiden - und wann sollten wir jede Art von Unterabfrage verwenden? Stellen wir zunächst sicher, dass wir verstehen, wie diese zweite Abfrage funktioniert. Wir haben zwei Tabellen in der from -Klausel unserer Abfrage ausgewählt, dann eine Abfrage geschrieben und der Datenbank mitgeteilt, dass es sich tatsächlich um eine Tabelle handelt - mit der die Datenbank vollkommen zufrieden ist. Es gibt kann einige Vorteile bei der Verwendung dieser Methode (sowie einige Einschränkungen). In erster Linie lief diese Unterabfrage einmal. Wenn unsere Datenbank ein großes Datenvolumen enthält, könnte es durchaus eine massive Verbesserung gegenüber der ersten Methode geben. Da wir dies jedoch als Tabelle verwenden, müssen wir zusätzliche Datenzeilen einfügen, damit sie tatsächlich wieder mit unseren Datenzeilen verknüpft werden können. Wir müssen auch sicherstellen, dass genug Datenzeilen vorhanden sind, wenn wir einen einfachen Join wie in der obigen Abfrage verwenden. Wenn Sie sich erinnern, werden beim Join nur Zeilen mit übereinstimmenden Daten auf beiden Seiten des Joins zurückgezogen. Wenn wir nicht aufpassen, kann dies dazu führen, dass gültige Daten nicht aus unserer Fahrzeugtabelle zurückgegeben werden, wenn in dieser Unterabfrage keine übereinstimmende Zeile vorhanden ist.

Rückblickend auf die erste Unterabfrage gibt es auch einige Einschränkungen. Da wir Daten zurück in eine einzelne Zeile ziehen, können wir ONLY eine Datenzeile zurückziehen. In der select -Klausel einer Abfrage verwendete Unterabfragen verwenden häufig nur eine Aggregatfunktion wie sum, count, max oder eine andere ähnliche Aggregatfunktion. Sie tun nicht müssen, aber so werden sie oft geschrieben.

Bevor wir fortfahren, werfen wir einen kurzen Blick darauf, wo wir eine Unterabfrage noch verwenden können. Wir können es in der where -Klausel verwenden - jetzt ist dieses Beispiel ein wenig erfunden, da es in unserer Datenbank bessere Möglichkeiten gibt, die folgenden Daten abzurufen, aber da es sich nur um ein Beispiel handelt, werfen wir einen Blick darauf :

select
    ID,
    brand
from
    brands
where
    brand like '%o%'

+----+--------+
| ID | brand  |
+----+--------+
|  1 | Ford   |
|  2 | Toyota |
|  6 | Holden |
+----+--------+
3 rows in set (0.00 sec)

Dies gibt uns eine Liste von Marken-IDs und Markennamen zurück (die zweite Spalte wird nur hinzugefügt, um uns die Marken anzuzeigen), die den Buchstaben o im Namen enthalten.

Nun können wir die Ergebnisse dieser Abfrage in einer where-Klausel verwenden:

select
    a.ID,
    b.brand
from
    cars a
        join brands b
            on a.brand=b.ID
where
    a.brand in
        (
        select
            ID
        from
            brands
        where
            brand like '%o%'
        )

+----+--------+
| ID | brand  |
+----+--------+
|  2 | Ford   |
|  7 | Ford   |
|  1 | Toyota |
|  5 | Toyota |
|  8 | Toyota |
|  9 | Toyota |
| 11 | Toyota |
+----+--------+
7 rows in set (0.00 sec)

Wie Sie sehen, enthielt unsere Fahrzeugtabelle, obwohl die Unterabfrage die drei Marken-IDs zurückgab, nur Einträge für zwei von ihnen.

In diesem Fall funktioniert die Unterabfrage für weitere Details so, als hätten wir den folgenden Code geschrieben:

select
    a.ID,
    b.brand
from
    cars a
        join brands b
            on a.brand=b.ID
where
    a.brand in (1,2,6)

+----+--------+
| ID | brand  |
+----+--------+
|  1 | Toyota |
|  2 | Ford   |
|  5 | Toyota |
|  7 | Ford   |
|  8 | Toyota |
|  9 | Toyota |
| 11 | Toyota |
+----+--------+
7 rows in set (0.00 sec)

Wieder können Sie sehen, wie eine Unterabfrage im Vergleich zu manuellen Eingaben die Reihenfolge der Zeilen geändert hat, wenn Sie von der Datenbank zurückkehren.

Lassen Sie uns während der Diskussion von Unterabfragen sehen, was wir mit einer Unterabfrage noch tun können:

  • Sie können eine Unterabfrage in eine andere Unterabfrage einfügen usw. Es gibt ein Limit, das von Ihrer Datenbank abhängt, aber abgesehen von den rekursiven Funktionen einiger verrückter und verrückter Programmierer werden die meisten Leute dieses Limit nie erreichen.
  • Sie können eine Reihe von Unterabfragen in eine einzelne Abfrage einfügen, einige in der select -Klausel, einige in der from -Klausel und einige weitere in der where -Klausel Wenn Sie eine Abfrage eingeben, wird diese komplexer und die Ausführung dauert wahrscheinlich länger.

Wenn Sie einen effizienten Code schreiben müssen, kann es vorteilhaft sein, die Abfrage auf verschiedene Arten zu schreiben und zu ermitteln (entweder durch Timing oder Verwendung eines EXPLAIN-Plans), welche Abfrage für die Erzielung Ihrer Ergebnisse optimal ist. Der erste Weg, der funktioniert, ist möglicherweise nicht immer der beste.

62
Fluffeh

Teil 3 - Tricks und effizienter Code

MySQL in () Effizienz

Ich dachte, ich würde ein paar zusätzliche Teile hinzufügen, um Tipps und Tricks zu erhalten, die sich ergeben haben.

Eine Frage, die mir ein wenig auffällt, ist Wie erhalte ich nicht übereinstimmende Zeilen aus zwei Tabellen und ich sehe die am häufigsten akzeptierte Antwort als etwas wie folgt (basierend auf unserer Auto- und Markentabelle - die Holden als Marke aufführt, aber nicht in der Autotabelle erscheint):

select
    a.ID,
    a.brand
from
    brands a
where
    a.ID not in(select brand from cars)

Und ja es wird funktionieren.

+----+--------+
| ID | brand  |
+----+--------+
|  6 | Holden |
+----+--------+
1 row in set (0.00 sec)

Es ist jedoch in einigen Datenbanken nicht effizient. Hier ist ein Link zu einer Stapelüberlauf-Frage und hier ist ein ausführlicher Artikel , wenn Sie sich auf das Wesentliche einlassen möchten.

Die kurze Antwort lautet: Wenn das Optimierungsprogramm nicht effizient damit umgeht, ist es möglicherweise besser, eine Abfrage wie die folgende zu verwenden, um nicht übereinstimmende Zeilen zu erhalten:

select
    a.brand
from
    brands a
        left join cars b
            on a.id=b.brand
where
    b.brand is null

+--------+
| brand  |
+--------+
| Holden |
+--------+
1 row in set (0.00 sec)

Aktualisieren Sie die Tabelle mit derselben Tabelle in der Unterabfrage

Ahhh, ein anderer Oldie außer Goodie - der alte Sie können in der FROM-Klausel keine Zieltabellen-Marken für die Aktualisierung angeben.

In MySQL können Sie keine update... -Anfrage mit einer Unterauswahl in derselben Tabelle ausführen. Sie überlegen sich vielleicht, warum Sie es nicht einfach in die where-Klausel einfügen, oder? Aber was ist, wenn Sie nur die Zeile mit dem Datum max() unter einer Reihe anderer Zeilen aktualisieren möchten? Das kann man in einer where-Klausel nicht genau machen.

update 
    brands 
set 
    brand='Holden' 
where 
    id=
        (select 
            id 
        from 
            brands 
        where 
            id=6);
ERROR 1093 (HY000): You can't specify target table 'brands' 
for update in FROM clause

Also können wir das nicht machen, oder? Nicht genau. Es gibt eine hinterhältige Problemumgehung, von der eine überraschend große Anzahl von Benutzern nichts weiß - obwohl sie einige Hacker enthält, auf die Sie achten müssen.

Sie können die Unterabfrage in eine andere Unterabfrage einfügen, wodurch eine ausreichende Lücke zwischen den beiden Abfragen entsteht, damit sie funktioniert. Beachten Sie jedoch, dass es möglicherweise am sichersten ist, die Abfrage innerhalb einer Transaktion zu fixieren. Dadurch wird verhindert, dass während der Ausführung der Abfrage weitere Änderungen an den Tabellen vorgenommen werden.

update 
    brands 
set 
    brand='Holden' 
where id=
    (select 
        id 
    from 
        (select 
            id 
        from 
            brands 
        where 
            id=6
        ) 
    as updateTable);

Query OK, 0 rows affected (0.02 sec)
Rows matched: 1  Changed: 0  Warnings: 0
58
Fluffeh

Sie können das Konzept mehrerer Abfragen im Schlüsselwort FROM verwenden. Lassen Sie mich Ihnen ein Beispiel zeigen:

SELECT DISTINCT e.id,e.name,d.name,lap.lappy LAPTOP_MAKE,c_loc.cnty COUNTY    
FROM  (
          SELECT c.id cnty,l.name
          FROM   county c, location l
          WHERE  c.id=l.county_id AND l.end_Date IS NOT NULL
      ) c_loc, emp e 
      INNER JOIN dept d ON e.deptno =d.id
      LEFT JOIN 
      ( 
         SELECT l.id lappy, c.name cmpy
         FROM   laptop l, company c
         WHERE l.make = c.name
      ) lap ON e.cmpy_id=lap.cmpy

Sie können beliebig viele Tabellen verwenden. Verwenden Sie äußere Verknüpfungen und Vereinigungen, wo immer dies erforderlich ist, auch innerhalb von Tabellenunterabfragen.

Das ist eine sehr einfache Methode, um so viele Tabellen und Felder einzubeziehen.

18
prashant1988

Hoffe, dass es die Tabellen findet, während Sie die Sache durchlesen:

jsfiddle

mysql> show columns from colors;                                                         
+-------+-------------+------+-----+---------+----------------+
| Field | Type        | Null | Key | Default | Extra          |
+-------+-------------+------+-----+---------+----------------+           
| id    | int(3)      | NO   | PRI | NULL    | auto_increment |
| color | varchar(15) | YES  |     | NULL    |                |
| Paint | varchar(10) | YES  |     | NULL    |                |
+-------+-------------+------+-----+---------+----------------+
6
Anton Chan