Zapytanie SQL zwraca dane z wielu tabel

Chciałbym wiedzieć co następuje:

  • Jak uzyskać dane z wielu tabel w mojej bazie danych?
  • Jakie są metody, aby to zrobić?
  • Czym są połączenia i związki i czym się od siebie różnią?
  • Kiedy powinienem używać każdego z nich w porównaniu do innych?

Planuję użyć tego w mojej (na przykład - PHP) aplikacji, ale nie chcę uruchamiać wielu zapytań z bazy danych, z jakich opcji muszę pobierać dane wiele tabel w jednym zapytaniu?

Uwaga: piszę to, ponieważ chciałbym być w stanie połączyć się z dobrze napisanym przewodnikiem na liczne pytania, które ciągle natykam się w kolejce PHP, więc mogę link do tego, aby uzyskać więcej szczegółów, gdy zamieszczam odpowiedź.

Odpowiedzi obejmują:

  1. Część 1-Połączenia i związki
  2. Część 2-Subqueries
  3. Część 3-sztuczki i skuteczny kod
  4. Część 4 - Zapytania podrzędne w klauzuli From
  5. Część 5-mieszana torba sztuczek Johna
Author: Fluffeh, 2012-09-18

6 answers

Część 1-Połączenia i związki

Ta odpowiedź obejmuje:

  1. Część 1
      W 1999 roku w ramach projektu "tworzenie nowych tabel" w ramach projektu "tworzenie nowych tabel" w ramach projektu "tworzenie nowych tabel" w ramach projektu "tworzenie nowych tabel" w ramach projektu "tworzenie nowych tabel" w ramach projektu "tworzenie nowych tabel" w ramach projektu "tworzenie nowych tabel" w ramach projektu "tworzenie nowych tabel"]}
    • Jak korzystać z zapytania Unii
    • LEWE I Prawe Złącza zewnętrzne (ta odpowiedź stackOverflow jest doskonała do opisania typów połączeń)
    • Intersect queries (i jak je odtworzyć, jeśli baza danych ich nie obsługuje) - jest to funkcja SQL-Server (zobacz info ) i część powodu, dla którego napisałem to wszystko w pierwszej kolejności.
  2. część 2
    • Subqueries - czym są, gdzie można ich używać i na co uważać]}
    • Kartezjusz dołączył AKA-Oh, the misery!

Istnieje wiele sposobów pobierania danych z wielu tabel w bazie danych. W tej odpowiedzi będę używał składni ANSI-92 join. Może się to różnić od kilku inne samouczki, które używają starszej składni ANSI-89 (a jeśli jesteś przyzwyczajony do 89, mogą wydawać się znacznie mniej intuicyjne - ale wszystko, co mogę powiedzieć, to spróbować), ponieważ jest {116]}dużo {117]} łatwiejsze do zrozumienia, gdy zapytania zaczynają się bardziej złożone. Po co go używać? Czy jest poprawa wydajności? krótka odpowiedź brzmi nie, ale jest łatwiejsza do odczytania, gdy się do niej przyzwyczaisz. Łatwiej jest odczytać zapytania napisane przez innych ludzi używających tej składni.

Zamierzam również użyć koncepcja małego warsztatu samochodowego, który ma bazę danych do śledzenia dostępnych samochodów. Właściciel zatrudnił cię jako swojego informatyka i oczekuje, że będziesz w stanie upuścić mu dane, o które prosi w mgnieniu oka.

Zrobiłem kilka tabel wyszukiwania, które będą używane przez tabelę końcową. To da nam rozsądny model do pracy. Na początek będę uruchamiał moje zapytania na przykładowej bazie danych, która ma następującą strukturę. Postaram się myśleć o typowe błędy, które są popełniane na początku i wyjaśnić, co z nimi nie tak - a także oczywiście pokazując, jak je poprawić.

Pierwsza tabela to po prostu lista kolorów, dzięki czemu wiemy, jakie kolory mamy na podwórku samochodu.

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)

Tabela marek określa różne marki samochodów, które mogą być sprzedawane przez caryard.

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)

Tabela modeli obejmie różne typy samochodów, łatwiej będzie korzystać z różnych typów samochodów zamiast rzeczywistych modeli samochodów.

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)

I wreszcie, aby związać wszystkie inne Stoły, stół, który łączy wszystko razem. Pole ID jest w rzeczywistości unikalnym numerem partii używanym do identyfikacji samochodów.

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)

To da nam wystarczająco dużo danych (mam nadzieję), aby przykryć poniższe przykłady różnych typów połączeń, a także dać wystarczająco dużo danych, aby były warte zachodu.

Więc wchodząc w to zamieszanie, szef chce znać identyfikatory wszystkich sportowych samochodów, które miał. has .

Jest to proste połączenie dwóch tabel. Mamy tabelę, która identyfikuje model i tabelę z dostępnymi zapasami w nim. Jak widać, dane w kolumnie {[26] } tabeli cars odnoszą się do kolumny models tabeli cars, którą mamy. Teraz wiemy, że tabela modeli ma ID 1 dla Sports, więc zapisujmy join.

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

Więc to zapytanie wygląda dobrze, prawda? Zidentyfikowaliśmy dwie tabele i zawierają potrzebne nam informacje i korzystamy z połączenia to poprawnie określa, które kolumny połączyć.

ERROR 1052 (23000): Column 'ID' in field list is ambiguous
Oh noes! Błąd w naszym pierwszym zapytaniu! Tak, i to jest śliwka. Widzisz, zapytanie rzeczywiście ma właściwe kolumny, ale niektóre z nich istnieją w obu tabelach, więc baza danych jest zdezorientowana co do tego, co mamy na myśli i gdzie. Istnieją dwa rozwiązania tego problemu. Pierwszy jest prosty i przyjemny, możemy użyć tableName.columnName, aby powiedzieć bazie danych dokładnie, co mamy na myśli, w następujący sposób:
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)

Drugi jest prawdopodobnie częściej używany i nazywa się aliasing tabeli. Tabele w tym przykładzie mają ładne i krótkie proste nazwy, ale wpisanie czegoś w stylu KPI_DAILY_SALES_BY_DEPARTMENT prawdopodobnie szybko się zestarzeje, więc prostym sposobem jest nazwanie tabeli w ten sposób: {]}

select
    a.ID,
    b.model
from
    cars a
        join models b
            on a.model=b.ID
Wróćmy do prośby. Jak widać mamy informacje, których potrzebujemy, ale mamy również informacje, o które nie poproszono, więc musimy zawrzeć klauzulę where w oświadczeniu, aby uzyskać tylko samochody sportowe, o które poproszono. Jako że wolę metodę table alias raczej niż używanie nazw tabel w kółko, będę się go trzymać od tego momentu.

Oczywiście, musimy dodać klauzulę where do naszego zapytania. Samochody sportowe możemy rozpoznać po ID=1 lub model='Sports'. Ponieważ ID jest indeksowane i klucz główny (i zdarza się, że jest mniej wpisywania), możemy użyć tego w naszym zapytaniu.

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! Szef jest szczęśliwy. Oczywiście, będąc szefem i nigdy nie będąc zadowolonym z tego, o co prosił, patrzy na informacje, a następnie mówi[116]}chcę kolorów jako cóż .

Ok, więc mamy już napisaną część naszego zapytania, ale musimy użyć trzeciej tabeli, która jest kolorami. Teraz nasza główna tabela informacyjna cars Przechowuje identyfikator koloru samochodu, a to odsyła do kolumny colors ID. Tak więc, w sposób podobny do oryginału, możemy dołączyć trzecią tabelę: {]}

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)

Cholera, chociaż tabela została poprawnie połączona i powiązane kolumny zostały połączone, zapomnieliśmy wyciągnąć rzeczywiste informacje z nowej tabeli, że po prostu połączone.

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)
/ Align = "left" / Teraz wyjaśnię to trochę bardziej szczegółowo. Jak widzisz, klauzula from w naszym oświadczeniu łączy naszą główną tabelę (często używam tabeli zawierającej informacje, a nie tabeli lookup lub tabeli wymiarów. Zapytanie będzie działać tak samo dobrze z tabelami, ale ma mniej sensu, gdy wrócimy do tego zapytania, aby przeczytać go w ciągu kilku miesięcy, więc często najlepiej jest spróbować napisać zapytanie to będzie miłe i łatwe do zrozumienia - ułóż je intuicyjnie, użyj ładnego wcięcia, aby wszystko było tak jasne, jak to tylko możliwe. Jeśli dalej uczysz innych, spróbuj zaszczepić te cechy w ich zapytaniach - zwłaszcza jeśli będziesz ich rozwiązywać.

Jest całkowicie możliwe łączenie coraz większej liczby tabel w ten sposób.

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

Chociaż zapomniałem dodać tabelę, w której możemy chcieć połączyć więcej niż jedną kolumnę w instrukcji join, oto przykład. Jeśli tabela models miała modele specyficzne dla marki, a zatem miała również kolumnę o nazwie brand, która łączyła się z tabelą brands w polu ID, można to zrobić tak:

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

Powyższe zapytanie nie tylko łączy połączone tabele z główną tabelą cars, ale także określa połączenia pomiędzy już połączonymi tabelami. Jeśli tego nie zrobiono, wynik nazywa się połączeniem kartezjańskim - czyli dba mówi źle. Złącze kartezjańskie to takie, w którym zwracane są wiersze ponieważ informacje nie mówią bazie danych, jak ograniczyć wyniki, więc zapytanie zwraca wszystkie wiersze, które pasują do kryteriów.

Więc, aby podać przykład złączenia kartezjańskiego, uruchom następujące zapytanie:

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)
Dobry Boże, to brzydkie. Jednakże, jeśli chodzi o bazę danych, to jest to dokładnie, o co poproszono. W zapytaniu poprosiliśmy o ID Z cars i model z models. Jednakże, ponieważ nie sprecyzowaliśmy jak aby dołączyć tabele, baza danych dopasowała co wiersz z pierwszej tabeli z co wiersz z drugiej tabeli. / Align = "center" bgcolor = "# e0ffe0 " / król Danii / / align = center / chcę tę samą listę, ale dołącz do niej również 4WDs . To jednak daje nam świetną wymówkę, by przyjrzeć się dwóm różnym sposobom osiągnięcia tego celu. Możemy dodać kolejny warunek do klauzuli where w ten sposób:
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

Podczas gdy powyżej będzie działać doskonale, spójrzmy na to inaczej, jest to świetna wymówka, aby pokazać, jak będzie działać zapytanie union.

Wiemy, że następujące zwrócą wszystkie sportowe samochody:

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

I następujące zwróci wszystkie 4WDs:

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

Tak więc dodając klauzulę union all pomiędzy nimi, wyniki drugiego zapytania zostaną dołączone do wyników pierwszego zapytania.

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)

Jak widać, wyniki pierwszego zapytania są zwracane jako pierwsze, następnie wyniki drugiego zapytania.

W tym przykładzie byłoby oczywiście znacznie łatwiej po prostu użyć pierwszego zapytania, ale union zapytania mogą być świetne dla konkretnych przypadków. Są one świetnym sposobem na zwrócenie konkretnych wyników z tabel z tabel, które nie są łatwo ze sobą połączone - lub w tym przypadku całkowicie niepowiązanych tabel. Istnieje jednak kilka zasad do przestrzegania.

  • typy kolumn z pierwszego zapytania muszą być zgodne z typami kolumn z każdego innego zapytania poniżej.
  • nazwy kolumn z pierwszego zapytania zostaną użyte do identyfikacji całego zestawu wyników.
  • Liczba kolumn w każdym zapytaniu musi być taka sama.

Teraz możesz zastanawiać się, jaka jest różnica między używaniem union a union all. Zapytanie union usunie duplikaty, podczas gdy zapytanie union all nie usunie. Oznacza to, że jest mały hit wydajności przy użyciu union over union all, ale wyniki mogą być warte Nie będę spekulował na ten temat.

W tej notce, może warto zwrócić uwagę na kilka dodatkowych uwag tutaj.

  • jeśli chcemy zamówić wyniki, możemy użyć order by, ale nie możesz już używać aliasu. W powyższym zapytaniu dodanie order by a.ID spowodowałoby błąd-jeśli chodzi o wyniki, kolumna nazywa się ID, a nie a.ID - mimo że w obu zapytaniach użyto tego samego aliasu.
  • możemy mieć tylko jedną order by oświadczenie, i to musi być jako ostatnie oświadczenie.

W następnych przykładach dodaję kilka dodatkowych wierszy do naszych tabel.

Dodałem Holden do tabeli marek. Dodałem również wiersz do cars, który ma color wartość 12 - który nie ma odniesienia w tabeli kolorów.

/ Align = "center" bgcolor = "# e0ffe0 " / cesarz Chin / / align = center / '- Typowe, po prostu dostajemy się do interesującego część naszej dyskusji i szef chce więcej pracy.

Racja, więc pierwszą rzeczą, którą musimy zrobić, to uzyskać pełną listę możliwych marek.

select
    a.brand
from
    brands a

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

Teraz, kiedy dołączymy to do naszej tabeli samochodów, otrzymamy następujący wynik:

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)

Co jest oczywiście problemem - nie widzimy żadnej wzmianki o uroczej Holden marce, którą dodałem.

To dlatego, że połączenie szuka pasujących wierszy w obu tabelach. Ponieważ nie ma danych w samochodach, które są typu Nie jest zwracana. Tutaj możemy użyć outer join. To zwróci wszystkie wyniki z jednej tabeli niezależnie od tego, czy są one dopasowane w drugiej tabeli, czy nie:

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)

Teraz, kiedy to mamy, możemy dodać uroczą funkcję agregacji, aby policzyć i zdjąć szefa z nas na chwilę.

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)

I z tym, away szef skulks.

Teraz, aby wyjaśnić to bardziej szczegółowo, zewnętrzne połączenia mogą być typu left lub right. / Align = "Left" / Prawo określa, która tabela jest w pełni włączona. A left outer join będzie zawierać wszystkie wiersze z tabeli po lewej stronie, podczas gdy (zgadłeś) a right outer join przeniesie wszystkie wyniki z tabeli po prawej stronie do wyników.

Niektóre bazy danych zezwalają na full outer join, które zwracają wyniki (dopasowane lub nie) z obu tabel, ale nie jest to obsługiwane we wszystkich bazach danych.

Teraz, prawdopodobnie domyślam się w tym momencie, zastanawiasz się, czy jesteś można łączyć typy przyłączeń w zapytaniu - a odpowiedź brzmi tak, absolutnie można.

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)

Więc dlaczego nie tego oczekiwano? Dzieje się tak dlatego, że chociaż wybraliśmy zewnętrzne połączenie od samochodów do marek, nie zostało ono określone w połączeniu do kolorów - tak, że konkretne połączenie przywróci tylko wyniki, które pasują do obu tabel.

Oto zapytanie, które zadziała, aby uzyskać oczekiwane wyniki:

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)

Jak widać, mamy dwa zewnętrzne złącza w zapytanie i wyniki idą zgodnie z oczekiwaniami.

A co z innymi rodzajami połączeń, o które pytasz? A co z skrzyżowaniami?

Cóż, nie wszystkie bazy danych obsługują intersection, ale prawie wszystkie bazy danych pozwolą Ci stworzyć przecięcie poprzez join (lub przynajmniej dobrze skonstruowane polecenie where).

Przecięcie jest rodzajem połączenia nieco podobnego do {[48] } jak opisano powyżej - ale różnica polega na tym, że tylko zwraca rzędy danych, które są identyczne (i mam na myśli identyczne) między różnymi indywidualnymi zapytaniami połączonymi przez Unię. Zwracane będą tylko wiersze, które są identyczne pod każdym względem.

Prosty przykład byłby taki:

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

Podczas gdy zwykłe zapytanie union zwróci wszystkie wiersze tabeli (pierwsze zapytanie zwraca cokolwiek ponad ID>2, a drugie cokolwiek ma ID<4), co spowoduje pełny zestaw, zapytanie intersektowe zwróci tylko pasujący wiersz id=3 ponieważ spełnia oba kryteria.

Teraz, jeśli twoja baza danych nie obsługuje zapytania intersect, powyższe można łatwo połączyć z następującym zapytaniem:

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)

Jeśli chcesz wykonać przecięcie dwóch różnych tabel przy użyciu bazy danych, która nie obsługuje zapytania przecięcia, musisz utworzyć połączenie na każdej kolumnie tabel.

 411
Author: Fluffeh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:26:38

Ok, uznałem ten post za bardzo interesujący i chciałbym podzielić się swoją wiedzą na temat tworzenia zapytania. Dzięki za to Fluffeh. Inni, którzy mogą to przeczytać i mogą czuć, że się mylę, są w 101% wolni, aby edytować i krytykować moją odpowiedź. (szczerze, czuję się bardzo wdzięczny za naprawienie mojego błędu(s).)

Będę zamieszczać niektóre z najczęściej zadawanych pytań w tagu MySQL.


Trick nr 1 (wiersze pasujące do wielu warunki)

Biorąc pod uwagę ten schemat

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

Pytanie

Znajdź wszystkie filmy które należą do co najmniej zarówno Comedy i Romance kategorii.

Rozwiązanie

To pytanie może być czasami bardzo trudne. Może się wydawać, że takie zapytanie będzie odpowiedzią:-
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

Co jest zdecydowanie bardzo złe, ponieważ nie daje żadnego wyniku . Na wyjaśnienie tego jest takie, że w każdym wierszu jest tylko jedna ważna wartość CategoryName. Na przykład pierwszy warunek zwraca true, drugi warunek zawsze jest fałszywy. Tak więc, używając operatora AND, oba warunki powinny być prawdziwe; w przeciwnym razie będzie to false. Kolejne zapytanie jest takie,

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

Sqlfiddle Demo

I wynik jest nadal niepoprawny, ponieważ pasuje do zapisu, który ma co najmniej jeden mecz na categoryName. Na realne rozwiązanie będzie przez zliczanie liczby instancji rekordów na film . Liczba instancji powinna być zgodna z całkowitą liczbą wartości podanych w warunku.

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 (odpowiedź)


Trick nr 2 (maksymalny rekord dla każdego wpisu)

Podany schemat,

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

Pytanie

Znajdź najnowsza wersja każdego oprogramowania. Wyświetl następujące kolumny: SoftwareName,Descriptions,LatestVersion (z kolumny VersionNo),DateReleased

Rozwiązanie

Niektórzy programiści SQL błędnie używają funkcji agregującej MAX(). Mają tendencję do tworzenia w ten sposób,

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

(większość RDBMS generuje błąd składni, ponieważ nie podaje niektórych niezagregowanych kolumn w group by klauzuli ). poprawne LatestVersion na każdym oprogramowaniu, ale oczywiście {[18] } są nieprawidłowe. MySQL nie obsługuje Window Functions i Common Table Expression jeszcze tak jak niektóre RDBM. Obejściem tego problemu jest utworzenie subquery, która pobiera Indywidualne maksimum versionNo dla każdego oprogramowania, a następnie zostanie połączona z innymi tabelami.

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 (odpowiedź)


Więc to było to. Jak tylko przypomnę sobie inne FAQ na MySQL tag. Dziękuję za przeczytanie ten mały artykuł. Mam nadzieję, że przynajmniej trochę się z tego dowiesz.

UPDATE 1


Trick nr 3 (znalezienie najnowszego rekordu między dwoma identyfikatorami)

Podany Schemat

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

Pytanie

Znajdź ostatnią rozmowę między dwoma użytkownikami.

Rozwiązanie

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

 94
Author: John Woo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:26:38

Część 2-Podzakresy

Dobra, teraz szef znowu się włamał-chcę listę wszystkich naszych samochodów z tą marką i w sumie ile mamy tej marki!

Jest to świetna okazja, aby wykorzystać kolejny trick w naszym worku gadżetów SQL-subquery. Jeśli nie znasz tego terminu, zapytanie podrzędne jest zapytaniem uruchamianym wewnątrz innego zapytania. Istnieje wiele różnych sposobów ich wykorzystania.

Dla naszej prośby, najpierw umieścić proste zapytanie razem to będzie lista każdego samochodu i marki:

select
    a.ID,
    b.brand
from
    cars a
        join brands b
            on a.brand=b.ID
Jeśli chcemy po prostu uporządkować liczbę samochodów według marek, możemy oczywiście napisać to:]}
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 |
+--------+-----------+

Więc powinniśmy być w stanie po prostu dodać funkcję count do naszego oryginalnego zapytania, prawda?

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)
Niestety, nie, nie możemy tego zrobić. Powodem jest to, że gdy dodamy w ID samochodu (kolumna a.ID) musimy dodać go do grupy by - więc teraz, gdy funkcja count działa, jest tylko jedno ID dopasowane na Dowód osobisty.

Tutaj możemy jednak użyć subquery - w rzeczywistości możemy zrobić dwa zupełnie różne typy subquery, które zwrócą te same wyniki, których potrzebujemy do tego. Pierwszym z nich jest po prostu umieszczenie zapytania podrzędnego w klauzuli select. Oznacza to, że za każdym razem, gdy otrzymamy wiersz danych, zapytanie podrzędne uruchomi się, otrzyma kolumnę danych, a następnie wrzuci ją do naszego wiersza danych.

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)

I Bam! to by nam pomogło. Jeśli jednak zauważyłeś, to zapytanie podrzędne będzie musiało działać dla każdego jeden wiersz danych zwracamy. Nawet w tym małym przykładzie mamy tylko pięć różnych marek samochodów, ale zapytanie podrzędne działało jedenaście razy, ponieważ mamy jedenaście rzędów danych, które zwracamy. Więc w tym przypadku nie wydaje się to najskuteczniejszym sposobem pisania kodu.

Dla innego podejścia, uruchamiamy subquery i udajemy, że jest to tabela:

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)

Ok, więc mamy te same wyniki (uporządkowane nieco inaczej - wygląda na to, że baza chciała zwrócić wyniki uporządkowane według pierwszą kolumnę wybraliśmy tym razem) - ale te same prawidłowe liczby.

Więc, jaka jest różnica między tymi dwoma-i kiedy powinniśmy używać każdego typu subquery? Po pierwsze, upewnijmy się, że rozumiemy, jak działa to drugie zapytanie. Wybraliśmy dwie tabele w klauzuli from naszego zapytania, a następnie napisaliśmy zapytanie i powiedzieliśmy bazie danych, że w rzeczywistości jest to Tabela - z której baza danych jest całkowicie zadowolona. Nie Może być pewne korzyści z korzystania z tej metody (jak również niektóre ograniczenia). Najważniejsze jest to, że ten subquery uruchomił raz . Jeśli nasza baza danych zawierała dużą ilość danych, może nastąpić ogromna poprawa w stosunku do pierwszej metody. Jednak, ponieważ używamy tego jako tabeli, musimy wprowadzić dodatkowe wiersze danych - tak, że mogą one być rzeczywiście połączone z powrotem do naszych wierszy danych. Musimy również mieć pewność, że istnieje wystarczająco dużo wierszy danych, jeśli mamy użyć prostego łącznika, jak w powyższym zapytaniu. Jeśli pamiętasz, połączenie będzie ciągnąć tylko tylnych wierszy, które mają pasujące dane na obu stronach łącznika. Jeśli nie będziemy ostrożni, może to spowodować, że poprawne dane nie zostaną zwrócone z naszej tabeli cars, jeśli nie ma pasującego wiersza w tym zapytaniu podrzędnym.

Teraz, patrząc wstecz na pierwsze zapytanie podrzędne, są też pewne ograniczenia. ponieważ pobieramy dane z powrotem do jednego wiersza, możemy tylko pobrać jeden wiersz danych. Zapytania podrzędne używane w klauzuli select zapytania bardzo często używają tylko funkcji agregującej takie jak sum, count, max lub inną podobną funkcję agregacyjną. Nie mają do, ale często tak się pisze.

Zanim przejdziemy dalej, rzućmy okiem na to, gdzie jeszcze możemy użyć zapytania podrzędnego. Możemy go użyć w klauzuli where - teraz ten przykład jest trochę wymyślony, ponieważ w naszej bazie danych są lepsze sposoby uzyskania następujących danych, ale widząc, że jest to tylko dla przykładu, rzućmy okiem:

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

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

To zwraca nam listę marek Identyfikatory i nazwy marek (Druga kolumna jest dodawana tylko, aby pokazać nam marki), które zawierają literę o w nazwie.

Teraz możemy użyć wyników tego zapytania w klauzuli where:

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)

Jak widzisz, mimo że subquery zwracało trzy identyfikatory marek, nasza tabela samochodów miała wpisy tylko dla dwóch z nich.

W tym przypadku, dla dalszych szczegółów, zapytanie podrzędne działa tak, jakbyśmy napisali następujący kod:

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)

Ponownie, można zobaczyć, jak subquery vs manual inputs zmieniły kolejność wierszy podczas powrotu z bazy danych.

Podczas gdy omawiamy subqueries, zobaczmy, co jeszcze możemy zrobić z subquery:

  • możesz umieścić subquery w innym subquery, i tak dalej i tak dalej. Istnieje limit, który zależy od bazy danych, ale poza rekurencyjnymi funkcjami jakiegoś szalonego i maniakalnego programisty, większość ludzi nigdy nie osiągnie tego limitu.
  • możesz umieścić wiele zapytań podrzędnych w pojedyncze zapytanie, kilka w klauzuli select, kilka w klauzuli from i kilka więcej w klauzuli where - pamiętaj tylko, że każde z nich sprawia, że Twoje zapytanie jest bardziej złożone i prawdopodobnie zajmie więcej czasu.

Jeśli potrzebujesz napisać jakiś wydajny kod, może być korzystne napisać zapytanie na wiele sposobów i zobaczyć (albo przez timing go lub za pomocą planu wyjaśnij), które jest optymalne zapytanie, aby uzyskać wyniki. Pierwszy sposób, który działa, może nie zawsze być najlepszym sposobem o zrobieniu tego.

 59
Author: Fluffeh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-09-19 09:47:16

Część 3-sztuczki i skuteczny kod

MySQL in() efficiency

Pomyślałem, że dodam kilka dodatkowych kawałków, za wskazówki i sztuczki, które pojawiły się.

Jedno pytanie, jakie widzę, to Jak uzyskać niepasujące wiersze z dwóch tabel i widzę odpowiedź najczęściej akceptowaną jako coś w rodzaju poniższego (na podstawie tabeli naszych samochodów i marek - która ma Holden wymieniony jako marka, ale nie pojawia się w samochodach tabela):

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

I tak to zadziała.

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

Jednak jest Nie wydajny w jakiejś bazie danych. Oto link do pytania o przepełnienie stosu , a oto doskonały artykuł, jeśli chcesz dostać się do nitty gritty.

Krótka odpowiedź brzmi: jeśli optymalizator nie obsługuje go efektywnie, może być znacznie lepiej użyć zapytania, takiego jak poniżej, aby uzyskać niepasowane wiersze:

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)

Update Tabela z tą samą tabelą w subquery

Ahhh, kolejny stary, ale dobry-stary nie można określić tabeli docelowej "marki" do aktualizacji w klauzuli.

MySQL nie pozwoli Ci uruchomić zapytania update... z zaznaczeniem podrzędnym w tej samej tabeli. Może się zastanawiacie, dlaczego po prostu nie wrzucić tego do klauzuli where? Ale co zrobić, jeśli chcesz zaktualizować tylko wiersz z max() Data amoung kilka innych wierszy? Nie da się tego zrobić w miejscu, w którym klauzula.

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
Więc nie możemy tego zrobić? Niezupełnie. Istnieje podstępne obejście, o którym zaskakująco duża liczba użytkowników nie wie - choć zawiera pewne hackery, na które musisz zwrócić uwagę.

Możesz umieścić zapytanie podrzędne w innym zapytaniu podrzędnym, co daje wystarczającą lukę między dwoma zapytaniami, aby działało. Należy jednak pamiętać, że najbezpieczniej będzie umieścić zapytanie w transakcji - zapobiegnie to innym zmianom wykonane do tabel podczas wykonywania zapytania.

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
 55
Author: Fluffeh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:47:32

Możesz użyć pojęcia wielu zapytań w słowie kluczowym FROM. Pozwól, że pokażę Ci jeden przykład:

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

Możesz używać dowolnej liczby tabel. Używaj zewnętrznych złączeń i Unii tam, gdzie jest to konieczne, nawet wewnątrz zapytań podrzędnych tabeli.

Jest to bardzo łatwa metoda, aby zaangażować tak wiele, jak tabele i pola.

 16
Author: prashant1988,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-09-27 04:27:04

Nadzieję, że to sprawi, że znajdzie tabele podczas czytania rzeczy:

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    |                |
+-------+-------------+------+-----+---------+----------------+
 5
Author: Anton Chan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-05-19 09:33:40