465 Stimmen

SQL-Abfrage gibt Daten aus mehreren Tabellen zurück

Ich würde gerne Folgendes wissen:

  • Wie erhalte ich Daten aus mehreren Tabellen in meiner Datenbank?
  • Welche Arten von Methoden gibt es, um dies zu erreichen?
  • was sind joins und unions und wie unterscheiden sie sich voneinander?
  • Wann sollte ich die einzelnen Produkte im Vergleich zu den anderen einsetzen?

Ich plane, dies in meiner (zum Beispiel - PHP) Anwendung zu verwenden, aber nicht wollen, um mehrere Abfragen gegen die Datenbank ausführen, welche Optionen habe ich, um Daten aus mehreren Tabellen in einer einzigen Abfrage zu erhalten?

Anmerkung: Ich schreibe dies, weil ich gerne einen Link zu einem gut geschriebenen Leitfaden für die zahlreichen Fragen, die mir in der PHP-Warteschlange ständig begegnen, zur Verfügung stellen möchte, so dass ich darauf verweisen kann, wenn ich eine Antwort gebe.

Die Antworten decken folgende Bereiche ab:

  1. Teil 1 - Verbindungen und Zusammenschlüsse
  2. Teil 2 - Unterabfragen
  3. Teil 3 - Tricks und effizienter Code
  4. Teil 4 - Unterabfragen in der From-Klausel
  5. Teil 5 - Eine gemischte Tüte mit Johns Tricks

504voto

Fluffeh Punkte 32760

Teil 1 - Verbindungen und Zusammenschlüsse

Diese Antwort umfasst:

  1. Teil 1
    • Verknüpfung von zwei oder mehr Tabellen über einen Inner Join (siehe den Wikipedia-Eintrag für zusätzliche Informationen)
    • Wie man eine Gewerkschaftsabfrage verwendet
    • Linke und rechte äußere Verknüpfungen (diese stackOverflow-Antwort eignet sich hervorragend zur Beschreibung von Verbindungsarten)
    • Intersect-Abfragen (und wie man sie reproduziert, wenn Ihre Datenbank sie nicht unterstützt) - dies ist eine Funktion von SQL-Server ( siehe Info ) und Teil des der Grund, warum ich diese ganze Geschichte geschrieben habe an erster Stelle.
  2. Teil 2
    • Unterabfragen - was sie sind, wo sie verwendet werden können und worauf man achten muss
    • Cartesian joins AKA - Oh, das Elend!

Es gibt eine Reihe von Möglichkeiten, Daten aus mehreren Tabellen in einer Datenbank abzurufen. In dieser Antwort werde ich die ANSI-92 Join-Syntax verwenden. Dies unterscheidet sich vielleicht von einer Reihe anderer Tutorials, die die ältere ANSI-89-Syntax verwenden (und wenn Sie 89 gewohnt sind, mag es Ihnen weniger intuitiv erscheinen - aber ich kann nur sagen, probieren Sie es aus), denn es ist viel leichter zu verstehen, wenn die Abfragen komplexer werden. Warum es verwenden? Gibt es einen Leistungsgewinn? Die kurze Antwort ist nein, aber es es leichter zu lesen, wenn man sich daran gewöhnt hat. Es ist einfacher, Abfragen zu lesen, die von anderen Personen mit dieser Syntax geschrieben wurden.

Ich werde auch das Konzept eines kleinen Fahrzeugparks verwenden, der über eine Datenbank verfügt, um zu verfolgen, welche Fahrzeuge verfügbar sind. Der Eigentümer hat Sie als IT-Mitarbeiter eingestellt und erwartet von Ihnen, dass Sie ihm die gewünschten Daten auf Anhieb liefern können.

Ich habe eine Reihe von Nachschlagetabellen erstellt, die von der Abschlusstabelle verwendet werden sollen. Damit haben wir ein vernünftiges Modell, mit dem wir arbeiten können. Zu Beginn werde ich meine Abfragen mit einer Beispieldatenbank mit folgender Struktur durchführen. Ich werde versuchen, an häufige Fehler zu denken, die am Anfang gemacht werden, und erklären, was dabei schief läuft - und natürlich auch zeigen, wie man sie korrigiert.

Die erste Tabelle ist einfach eine Farbauflistung, damit wir wissen, welche Farben wir auf dem Autohof 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)

In der Markentabelle sind die verschiedenen Automarken aufgeführt, die unser Hof 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 wird verschiedene Fahrzeugtypen abdecken. Es wird einfacher sein, verschiedene Fahrzeugtypen zu verwenden, als tatsächliche Fahrzeugmodelle.

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 Tabellen miteinander zu verbinden, die Tabelle, die alles zusammenhält. Das ID-Feld ist die eindeutige Losnummer, 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)

Damit haben wir (hoffentlich) genug Daten, um die folgenden Beispiele für verschiedene Arten von Verbindungen abzudecken und auch genug Daten, um sie sinnvoll zu gestalten.

Um zur Sache zu kommen, möchte der Chef wissen Die IDs aller Sportwagen, die er besitzt .

Dies ist eine einfache Verknüpfung zweier Tabellen. Wir haben eine Tabelle, die das Modell identifiziert, und eine Tabelle, die den verfügbaren Bestand enthält. Wie Sie sehen können, sind die Daten in der Tabelle model Spalte der cars Tabelle bezieht sich auf die models Spalte der cars Tabelle haben wir. Wir wissen jetzt, dass die Tabelle models die ID 1 für Sports schreiben wir also die Verbindung.

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 eine Verknüpfung, die korrekt angibt, welche Spalten verknüpft werden sollen.

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

Oh nein! Ein Fehler in unserer ersten Abfrage! Ja, und es ist eine Pflaume. Die Abfrage hat zwar die richtigen Spalten, aber einige von ihnen sind in beiden Tabellen vorhanden, so dass die Datenbank verwirrt ist, welche Spalte wir eigentlich meinen und wo. Es gibt zwei Lösungen, um dieses Problem zu lösen. Die erste ist einfach und schön, wir können verwenden tableName.columnName um der Datenbank genau zu sagen, was wir meinen, etwa so:

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)

Die andere wird wahrscheinlich häufiger verwendet und heißt Tabellen-Aliasing. Die Tabellen in diesem Beispiel haben schöne und kurze einfache Namen, aber die Eingabe von etwas wie KPI_DAILY_SALES_BY_DEPARTMENT würde wahrscheinlich schnell langweilig werden, also ist eine einfache Möglichkeit, die Tabelle wie folgt zu benennen:

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

Nun zurück zu dem Antrag. Wie Sie sehen, haben wir die Informationen, die wir brauchen, aber wir haben auch Informationen, nach denen nicht gefragt wurde, also müssen wir eine Where-Klausel in die Anweisung aufnehmen, um nur die Sportwagen zu erhalten, nach denen gefragt wurde. Da ich die Tabellenalias-Methode bevorzuge, anstatt die Tabellennamen immer wieder zu verwenden, werde ich ab diesem Punkt dabei bleiben.

Wir müssen also eine Where-Klausel in unsere Abfrage aufnehmen. Wir können Sportwagen entweder identifizieren durch ID=1 o model='Sports' . Da die ID indiziert und der Primärschlüssel ist (und zufällig weniger typisiert ist), können wir sie 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 zufrieden. Da er natürlich ein Chef ist und nie mit dem zufrieden ist, was er verlangt hat, sieht er sich die Informationen an und sagt dann Ich möchte auch die Farben .

Okay, wir haben also einen großen Teil unserer Abfrage bereits geschrieben, aber wir müssen eine dritte Tabelle verwenden, nämlich die Farben. Nun, unsere Hauptinformationstabelle cars speichert die Farb-ID des Fahrzeugs und verknüpft diese mit der ID-Spalte der Farben. Auf ähnliche Weise wie im Original können wir also eine dritte Tabelle verbinden:

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 verbunden wurde und die zugehörigen Spalten verknüpft wurden, haben wir vergessen, die aktuelle Informationen aus der neuen Tabelle, die wir gerade verknüpft haben.

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)

Gut, damit ist der Chef für einen Moment von uns weg. Nun möchte ich Ihnen das Ganze etwas genauer erklären. Wie Sie sehen können, ist die from Klausel in unserer Anweisung verknüpft unsere Haupttabelle (ich verwende oft eine Tabelle, die Informationen enthält, und nicht eine Nachschlage- oder Dimensionstabelle. Die Abfrage würde genauso gut funktionieren, wenn die Tabellen vertauscht wären, aber weniger Sinn machen, wenn wir in ein paar Monaten zu dieser Abfrage zurückkehren, um sie zu lesen. Deshalb ist es oft am besten, eine Abfrage zu schreiben, die schön und leicht zu verstehen ist - sie intuitiv zu gestalten, schöne Einrückungen zu verwenden, damit alles so klar wie möglich ist. Wenn Sie später andere unterrichten, sollten Sie versuchen, diese Eigenschaften in ihre Abfragen einfließen zu lassen - vor allem, wenn Sie bei der Fehlersuche helfen werden.

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 einzubeziehen, in der wir vielleicht mehr als eine Spalte in der join Anweisung, hier ein Beispiel. Wenn die models Tabelle hatte markenspezifische Modelle und daher auch eine Spalte namens brand die mit der brands Tabelle auf der ID Feldes könnte man wie folgt vorgehen:

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 können, verbindet die obige Abfrage nicht nur die verbundenen Tabellen mit der Haupt cars Tabelle, sondern gibt auch Joins zwischen den bereits verbundenen Tabellen an. Wurde dies nicht getan, wird das Ergebnis als kartesische Verknüpfung bezeichnet - das ist DBA-Sprache für schlecht. Bei einer kartesischen Verknüpfung werden Zeilen zurückgegeben, weil die Datenbank nicht weiß, wie sie die Ergebnisse einschränken soll, so dass die Abfrage Folgendes zurückgibt alle die Zeilen, die die Kriterien erfüllen.

Um ein Beispiel für eine kartesische Verknüpfung zu geben, führen wir 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, ist das hässlich. Was jedoch die Datenbank betrifft, so ist sie genau was verlangt wurde. In der Abfrage haben wir nach den ID de cars et le model de models . Da wir jedoch nicht angegeben haben wie um die Tabellen zu verknüpfen, hat die Datenbank einen Abgleich jede Zeile aus der ersten Tabelle mit jede Zeile aus der zweiten Tabelle.

Okay, der Chef ist also wieder da und will wieder mehr Informationen. Ich möchte die gleiche Liste, aber auch 4WDs in ihr enthalten .

Dies gibt uns jedoch einen guten Grund, zwei verschiedene Möglichkeiten zu untersuchen, um dies zu erreichen. Wir könnten der Where-Klausel eine weitere Bedingung hinzufügen, etwa so:

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

Auch wenn das oben beschriebene perfekt funktioniert, ist dies eine gute Gelegenheit, um zu zeigen, wie eine union Abfrage wird funktionieren.

Wir wissen, dass die folgenden alle Sportwagen zurückbringen werden:

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ückbringen:

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

Durch Hinzufügen einer union all Klausel dazwischen, 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 können, werden die Ergebnisse der ersten Abfrage zuerst zurückgegeben, gefolgt von den Ergebnissen der zweiten Abfrage.

In diesem Beispiel wäre es natürlich viel einfacher gewesen, einfach die erste Abfrage zu verwenden, aber union Abfragen können für bestimmte Fälle sehr nützlich sein. Sie sind eine großartige Möglichkeit, um bestimmte Ergebnisse aus Tabellen zurückzugeben, die nicht einfach miteinander verbunden werden können - oder, was das betrifft vollständig unverbundene Tabellen. Es sind jedoch einige Regeln zu beachten.

  • Die Spaltentypen der ersten Abfrage müssen mit den Spaltentypen aller nachfolgenden 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.

Nun, Sie könnten sich fragen, was die Unterschied ist zwischen der Verwendung union y union all . A union Abfrage werden Duplikate entfernt, während eine union all wird nicht. Dies bedeutet, dass es einen kleinen Leistungsabfall gibt, wenn Sie unionunion all aber die Ergebnisse könnten es wert sein - darüber will ich hier aber nicht spekulieren.

In diesem Zusammenhang ist es vielleicht sinnvoll, einige zusätzliche Anmerkungen zu machen.

  • Wenn wir die Ergebnisse ordnen wollen, können wir eine order by aber Sie können den Alias nicht mehr verwenden. In der obigen Abfrage wird durch das Anhängen eines order by a.ID würde zu einem Fehler führen - was die Ergebnisse anbelangt, so heißt die Spalte ID よりも a.ID - auch wenn in beiden Abfragen derselbe Alias verwendet wurde.
  • Wir können nur einen haben order by Anweisung, und zwar als letzte Anweisung.

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

Ich habe hinzugefügt Holden zum Markentisch. Ich habe auch eine Zeile in cars die die color Wert von 12 - die keinen Verweis in der Farbtabelle hat.

Okay, der Chef ist wieder da und bellt Anfragen heraus - *Ich will eine Zählung jeder Marke, die wir führen, und die Anzahl der Autos, die sie haben!` - Typisch, wir kommen gerade zu einem interessanten Abschnitt unserer Diskussion und der Chef will mehr Arbeit.

Also, als Erstes müssen wir eine vollständige Liste 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 diese Tabelle nun mit der Tabelle "Autos" verknüpfen, 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 Holden Marke, die ich hinzugefügt habe.

Das liegt daran, dass eine Verknüpfung nach übereinstimmenden Zeilen in beide Tabellen. Da es in Autos keine Daten vom Typ Holden es wird nicht zurückgegeben. An dieser Stelle können wir eine outer beitreten. Dies ergibt alle die Ergebnisse aus einer Tabelle, 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)

Jetzt können wir eine schöne Aggregatfunktion hinzufügen, um eine Zählung zu erhalten und uns den Chef für einen Moment vom Hals zu schaffen.

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 schleicht sich der Chef davon.

Um dies näher zu erläutern, können äußere Verknüpfungen von der Art left o right Typ. Das Feld Links oder Rechts bestimmt, welche Tabelle vollständig enthalten. A left outer join enthält alle Zeilen der Tabelle auf der linken Seite, während (Sie haben es erraten) eine right outer join bringt alle Ergebnisse aus der Tabelle auf der rechten Seite in die Ergebnisse.

Einige Datenbanken erlauben eine full outer join die die Ergebnisse (ob übereinstimmend oder nicht) von beide Tabellen, aber dies wird nicht von allen Datenbanken unterstützt.

Wahrscheinlich fragen Sie sich jetzt, ob Sie Join-Typen in einer Abfrage zusammenführen können - 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 die Ergebnisse nicht so, wie sie erwartet wurden? Es liegt daran, dass wir zwar die äußere Verknüpfung von Autos zu Marken ausgewählt haben, diese aber nicht in der Verknüpfung zu Farben angegeben wurde - diese spezielle Verknüpfung liefert also nur Ergebnisse, die in beiden Tabellen übereinstimmen.

Hier ist die Abfrage, die die erwarteten Ergebnisse liefern würde:

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 werden wie erwartet angezeigt.

Was ist mit den anderen Arten von Verbindungen, fragen Sie? Was ist mit Überschneidungen?

Nun, nicht alle Datenbanken unterstützen die intersection aber so gut wie alle Datenbanken erlauben es Ihnen, eine Schnittmenge durch eine Verknüpfung (oder zumindest eine gut strukturierte Where-Anweisung) zu erstellen.

Eine Kreuzung ist eine Art von Verknüpfung, ähnlich wie eine union wie oben beschrieben - aber mit dem Unterschied, dass es seulement gibt Datenzeilen zurück, die zwischen den einzelnen Abfragen, die durch die Vereinigung verbunden sind, identisch sind (und ich meine wirklich identisch). Nur Zeilen, die in jeder Hinsicht identisch sind, werden zurückgegeben.

Ein einfaches Beispiel könnte so lauten:

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

Während eine normale union Abfrage würde alle Zeilen der Tabelle zurückgeben (die erste Abfrage, die etwas über ID>2 und das zweite etwas mit ID<4 ), was zu einer vollständigen Menge führen würde, würde eine Intersect-Abfrage nur die Zeile zurückgeben, die id=3 da sie beide Kriterien erfüllt.

Wenn Ihre Datenbank keine Unterstützung für eine intersect Abfrage kann die obige Abfrage leicht durch die folgende 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 Kreuzung zwischen zwei verschiedenen Tabellen durchführen möchten und dabei eine Datenbank verwenden, die nicht von Haus aus eine Kreuzungsabfrage unterstützt, müssen Sie eine Verknüpfung auf jede Spalte der Tabellen.

103voto

John Woo Punkte 249233

Ok, ich fand diesen Beitrag sehr interessant und möchte etwas von meinem Wissen über die Erstellung einer Abfrage weitergeben. Danke für diesen Beitrag Fluffeh . Andere, die dies lesen und der Meinung sind, dass ich falsch liege, können meine Antwort zu 101 % bearbeiten und kritisieren. ( Ehrlich gesagt, bin ich sehr dankbar für die Korrektur meiner Fehler. )

Ich werde einige der häufig gestellten Fragen unter MySQL Tag.


Trick Nr. 1 ( Zeilen, die auf mehrere Bedingungen zutreffen )

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');

QUESTION

finden. alle Filme die zu mindestens beide Comedy y Romance Kategorien.

Lösung

Diese Frage kann manchmal sehr knifflig sein. Es kann scheinen, dass eine Abfrage wie diese 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

was definitiv sehr falsch ist, weil es zu kein Ergebnis . Die Erklärung hierfür ist, dass es nur einen gültigen Wert für CategoryName en jede Zeile . Die erste Bedingung gibt zum Beispiel zurück wahr ist die zweite Bedingung immer falsch. Daher wird durch die Verwendung von AND Operator, sollten beide Bedingungen wahr sein, andernfalls ist sie falsch. Eine andere Abfrage sieht folgendermaßen aus,

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 einem Datensatz übereinstimmt, der 尠くも ein Spiel auf der categoryName . Die echte Lösung durch Zählen der Anzahl der Aufzeichnungsinstanzen pro Film . Die Anzahl der Instanzen 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');

QUESTION

Hier finden Sie die neueste Version der jeweiligen Software. Zeigen Sie die folgenden Spalten an: SoftwareName , Descriptions , LatestVersion ( aus der Spalte VersionNo ), DateReleased

Lösung

Einige SQL-Entwickler verwenden fälschlicherweise MAX() Aggregatfunktion. Sie neigen dazu, wie folgt zu erstellen,

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 in diesem Fall einen Syntaxfehler, weil einige der nicht aggregierten Spalten nicht in der Datei group by Klausel ) ergibt das Ergebnis die korrekte LatestVersion zu jeder Software, aber offensichtlich ist die DateReleased sind falsch. MySQL unterstützt nicht Window Functions y Common Table Expression noch nicht, wie es einige RDBMS bereits tun. Die Umgehung dieses Problems besteht in der Erstellung eines subquery die das individuelle Maximum erreicht versionNo auf jeder Software und später auf den anderen Tabellen verbunden werden.

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)


Das war's also. Ich werde einen weiteren Beitrag veröffentlichen, sobald ich mich an andere FAQ en MySQL Tag. Danke, dass Sie diesen kleinen Artikel gelesen haben. Ich hoffe, dass Sie zumindest ein wenig Wissen aus diesem Artikel mitnehmen konnten.

UPDATE 1


Trick Nr. 3 ( Suche nach dem letzten Datensatz 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');

QUESTION

Suchen Sie die letzte Unterhaltung 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

66voto

Fluffeh Punkte 32760

Teil 2 - Unterabfragen

Okay, jetzt ist der Chef wieder reingeplatzt - Ich möchte eine Liste aller unserer Autos mit der Marke und der Gesamtzahl der Fahrzeuge dieser Marke!

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

Für unsere Anfrage stellen wir zunächst eine einfache Abfrage zusammen, die jedes Auto und die Marke auflistet:

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

Wenn wir nun einfach nur die Anzahl der Autos sortiert nach Marke abfragen wollten, könnten 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, die Zählfunktion einfach zu unserer ursprünglichen Abfrage hinzuzufügen, richtig?

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 dafür ist, dass wir beim Hinzufügen der Fahrzeug-ID (Spalte a.ID) diese in die Gruppierung nach einbeziehen müssen, so dass jetzt, wenn die Zählfunktion funktioniert, nur eine ID pro ID übereinstimmt.

An dieser Stelle können wir jedoch eine Unterabfrage verwenden - und zwar zwei völlig unterschiedliche Arten von Unterabfragen, die die gleichen Ergebnisse liefern, die wir für diesen Zweck benötigen. Die erste ist, die Unterabfrage einfach in die select Klausel. Das bedeutet, dass jedes Mal, wenn wir eine Datenzeile erhalten, die Unterabfrage abläuft, eine Datenspalte abruft und sie 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 bumm, das würde uns reichen. Wie Sie jedoch feststellen konnten, muss diese Unterabfrage für jede einzelne Datenzeile, die wir zurückgeben, ausgeführt werden. Selbst in diesem kleinen Beispiel haben wir nur fünf verschiedene Automarken, aber die Unterabfrage wurde elfmal ausgeführt, da wir elf Datenzeilen zurückgeben. In diesem Fall scheint es also nicht die effizienteste Art zu sein, Code zu schreiben.

Für einen anderen Ansatz, lassen Sie uns eine Unterabfrage ausführen und so tun, als ob es eine Tabelle ist:

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 also die gleichen Ergebnisse (etwas anders geordnet - es scheint, dass die Datenbank die Ergebnisse nach der ersten Spalte, die wir dieses Mal ausgewählt haben, geordnet zurückgeben wollte) - aber die gleichen richtigen Zahlen.

Was ist also der Unterschied zwischen den beiden - und wann sollte man welche Art von Subquery verwenden? Stellen wir zunächst sicher, dass wir verstehen, wie diese zweite Abfrage funktioniert. Wir haben zwei Tabellen in der from Klausel unserer Abfrage, und schrieb dann eine Abfrage und teilte der Datenbank mit, dass es sich stattdessen um eine Tabelle handelt - womit die Datenbank vollkommen zufrieden ist. Dort kann Diese Methode hat einige Vorteile (aber auch einige Einschränkungen). In erster Linie läuft diese Unterabfrage einmal . Wenn unsere Datenbank eine große Menge an Daten enthielte, könnte es durchaus eine massive Verbesserung gegenüber der ersten Methode geben. Da es sich jedoch um eine Tabelle handelt, müssen wir zusätzliche Datenzeilen einfügen, damit sie mit unseren Datenzeilen verbunden werden können. Wir müssen auch sicherstellen, dass es genug Datenzeilen, wenn wir eine einfache Verknüpfung wie in der obigen Abfrage verwenden wollen. Wenn Sie sich erinnern, wird die Verknüpfung nur Zeilen zurückziehen, die übereinstimmende Daten auf beide Seiten der Verbindung. Wenn wir nicht vorsichtig sind, könnte dies dazu führen, dass keine gültigen Daten aus der Tabelle "Autos" zurückgegeben werden, wenn es keine passende Zeile in dieser Unterabfrage gibt.

Wenn wir uns die erste Unterabfrage ansehen, gibt es auch hier einige Einschränkungen. NUR eine Zeile der Daten zurückziehen. Unterabfragen, die in der select Klausel einer Abfrage sehr oft nur eine Aggregatfunktion verwenden, wie z. B. sum , count , max oder eine andere ähnliche Aggregatfunktion. Sie tun nicht haben zu, aber so werden sie oft geschrieben.

Bevor wir also weitermachen, schauen wir uns kurz an, wo wir sonst noch eine Unterabfrage verwenden können. Wir können sie verwenden in der where Klausel - dieses Beispiel ist ein wenig konstruiert, da es in unserer Datenbank bessere Möglichkeiten gibt, die folgenden Daten zu erhalten, aber da es nur ein Beispiel ist, sehen wir es uns an:

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 (die zweite Spalte wurde nur hinzugefügt, um uns die Marken zu zeigen), die den Buchstaben o im Namen.

Nun könnten wir die Ergebnisse dieser Abfrage in einer Where-Klausel wie folgt 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)

Sie sehen, dass die Unterabfrage zwar die drei Marken-IDs zurückgibt, die Tabelle Autos aber nur Einträge für zwei davon enthält.

In diesem Fall funktioniert die Unterabfrage 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)

Auch hier können Sie sehen, wie eine Unterabfrage im Vergleich zu manuellen Eingaben die Reihenfolge der Zeilen bei der Rückgabe aus der Datenbank verändert hat.

Da wir gerade über Unterabfragen sprechen, wollen wir sehen, was wir sonst noch mit einer Unterabfrage machen können:

  • Sie können eine Unterabfrage innerhalb einer anderen Unterabfrage platzieren, und so weiter und so fort. Es gibt eine Grenze, die von Ihrer Datenbank abhängt, aber wenn es sich nicht um rekursive Funktionen eines wahnsinnigen Programmierers handelt, werden die meisten Leute diese Grenze nie erreichen.
  • Sie können eine Reihe von Unterabfragen in einer einzigen Abfrage platzieren, ein paar in der select Klausel, einige in der from Klausel und ein paar weitere in der where Klausel - denken Sie daran, dass jede weitere Klausel Ihre Abfrage komplexer macht und die Ausführung länger dauern kann.

Wenn Sie einen effizienten Code schreiben müssen, kann es von Vorteil sein, die Abfrage auf mehrere Arten zu schreiben und zu sehen (entweder durch Zeitmessung oder durch Verwendung eines Explain-Plans), welche die optimale Abfrage ist, um Ihre Ergebnisse zu erhalten. Der erste Weg, der funktioniert, ist nicht immer der beste Weg, um es zu tun.

61voto

Fluffeh Punkte 32760

Teil 3 - Tricks und effizienter Code

MySQL in() Effizienz

Ich dachte, ich füge noch ein paar Tipps und Tricks hinzu, die sich ergeben haben.

Eine Frage, die immer wieder auftaucht, ist Wie erhalte ich nicht übereinstimmende Zeilen aus zwei Tabellen? und ich sehe die am häufigsten akzeptierte Antwort in etwa wie folgt (basierend auf unserer Auto- und Markentabelle - die Holden ist als Marke aufgeführt, erscheint aber nicht in der Autotabelle):

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

Und ja wird es funktionieren.

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

Es ist jedoch no in einer Datenbank effizient. Hier ist eine Link zu einer Stack Overflow-Frage gefragt, und hier ist eine exzellenter ausführlicher Artikel wenn Sie sich für die Einzelheiten interessieren.

Die kurze Antwort lautet: Wenn der Optimierer dies nicht effizient handhabt, ist es möglicherweise viel 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)

Tabelle aktualisieren mit derselben Tabelle in Unterabfrage

Ahhh, noch ein Oldie aber Goodie - der alte Sie können in der FROM-Klausel nicht die Zieltabelle 'brands' für die Aktualisierung angeben .

MySQL erlaubt es Ihnen nicht, eine update... Abfrage mit einer Unterauswahl für dieselbe Tabelle. Jetzt werden Sie vielleicht denken, warum nicht einfach in die Where-Klausel einfügen, oder? Aber was ist, wenn Sie nur die Zeile mit der max() Datum inmitten einer Reihe von anderen Zeilen? Das können Sie in einer Where-Klausel nicht wirklich tun.

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

Das können wir also nicht tun, oder? Nun, nicht ganz. Es gibt eine raffinierte Umgehung, die erstaunlich viele Nutzer nicht kennen - obwohl sie einige Hacks beinhaltet, die Sie beachten müssen.

Sie können die Unterabfrage in eine andere Unterabfrage einfügen, was einen ausreichenden Abstand zwischen den beiden Abfragen schafft, so dass es funktioniert. Beachten Sie jedoch, dass es am sichersten ist, die Abfrage in eine Transaktion einzubinden - dies verhindert, dass andere Änderungen an den Tabellen vorgenommen werden, während die Abfrage ausgeführt wird.

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

19voto

prashant1988 Punkte 262

Sie können das Konzept der Mehrfachabfragen im Schlüsselwort FROM verwenden. Ich möchte 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 so viele Tabellen verwenden, wie Sie möchten. Verwenden Sie Outer-Joins und Union, wo immer es nötig ist, auch innerhalb von Tabellenunterabfragen.

Das ist eine sehr einfache Methode, um so viele Tabellen und Felder wie möglich einzubeziehen.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X