Część 1 - Połączenia i związki
Ta odpowiedź obejmuje:
- Część 1
- Łączenie dwóch lub więcej tabel za pomocą łączenia wewnętrznego ( dodatkowe informacje można znaleźć we wpisie na Wikipedii )
- Jak korzystać z kwerendy związkowej
- Lewe i prawe połączenia zewnętrzne (ta odpowiedź StackOverflow doskonale nadaje się do opisywania rodzajów połączeń)
- Przecinaj zapytania (i jak je reprodukować, jeśli twoja baza danych ich nie obsługuje) - jest to funkcja SQL-Server ( patrz informacje ) i po części dlatego napisałem to wszystko .
- Część 2
- Podkwerendy - czym są, gdzie można ich używać i na co uważać
- Kartezjan dołącza do AKA - Och, nędza!
Istnieje wiele sposobów wyszukiwania danych z wielu tabel w bazie danych. W tej odpowiedzi użyję składni złączenia ANSI-92. Może się to różnić od wielu innych samouczków, które używają starszej składni ANSI-89 (a jeśli jesteś przyzwyczajony do 89, może wydawać się znacznie mniej intuicyjny - ale wszystko, co mogę powiedzieć, to wypróbować), ponieważ jest to o wiele łatwiejsze aby zrozumieć, kiedy zapytania stają się bardziej złożone. Po co z tego korzystać? Czy jest wzrost wydajności? Krótka odpowiedź nie jest, ale jest łatwiejsze do odczytania po przyzwyczaić się do niego. Łatwiej jest czytać zapytania napisane przez innych ludzi za pomocą tej składni.
Zamierzam również wykorzystać koncepcję małego caryarda, który ma bazę danych do śledzenia dostępnych samochodów. Właściciel wynajął cię jako swojego informatyka i oczekuje, że będziesz w stanie upuścić mu dane, o które prosi po kropli kapelusza.
Zrobiłem wiele tabel odnośników, które będą używane przy stole finałowym. To da nam rozsądny model do pracy. Na początek będę uruchamiać moje zapytania na przykładowej bazie danych o następującej strukturze. Spróbuję wymyślić typowe błędy popełniane na początku i wyjaśnić, co się z nimi dzieje - a także oczywiście pokażę, jak je naprawić.
Pierwszy stół to po prostu lista kolorów, dzięki czemu wiemy, jakie kolory mamy na podwórzu samochodowym.
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 identyfikuje różne marki samochodów, które caryard mógłby sprzedać.
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 niż z 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 te 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ącą ilość danych (mam nadzieję), aby zilustrować poniższe przykłady różnych rodzajów złączeń, a także dostarczy wystarczającą ilość danych, aby były warte zachodu.
Wchodząc w sedno, szef chce poznać identyfikatory wszystkich samochodów sportowych, które ma .
To proste połączenie dwóch tabel. Mamy tabelę, która identyfikuje model i tabelę z dostępnymi zapasami. Jak widać, dane w model
kolumnie cars
tabeli odnoszą się do models
kolumny cars
tabeli, którą mamy. Teraz wiemy, że tabela modeli ma identyfikator 1
dla, Sports
więc napiszmy złączenie.
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 informacje, a następnie używamy złączenia, które poprawnie identyfikuje kolumny, które należy dołączyć.
ERROR 1052 (23000): Column 'ID' in field list is ambiguous
Och nie! Błąd w naszym pierwszym zapytaniu! Tak, i to jest śliwka. Widzisz, zapytanie rzeczywiście ma odpowiednie kolumny, ale niektóre z nich istnieją w obu tabelach, więc baza danych jest zdezorientowana co do tego, którą kolumnę mamy na myśli i gdzie. Istnieją dwa rozwiązania tego problemu. Pierwszy jest ładny i prosty, możemy użyć tableName.columnName
bazy danych, aby dokładnie powiedzieć, 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 nazywany jest aliasingiem tabel. Tabele w tym przykładzie mają ładne i krótkie proste nazwy, ale wpisanie czegoś takiego KPI_DAILY_SALES_BY_DEPARTMENT
prawdopodobnie szybko się zestarzeje, więc prostym sposobem jest pseudonimowanie tabeli w ten sposób:
select
a.ID,
b.model
from
cars a
join models b
on a.model=b.ID
Wróćmy do żądania. Jak widać, mamy informacje, których potrzebujemy, ale mamy również informacje, o które nie poprosiliśmy, dlatego w oświadczeniu musimy zawrzeć klauzulę „where”, aby uzyskać tylko samochody sportowe, o które poproszono. Ponieważ wolę metodę aliasu tabeli niż używanie nazw tabel w kółko, odtąd będę się jej trzymał.
Oczywiście musimy dodać klauzulę where do naszego zapytania. Możemy zidentyfikować samochody sportowe za pomocą ID=1
lub model='Sports'
. Ponieważ identyfikator jest indeksowany, a klucz podstawowy (i zdarza się, że mniej się pisze), użyjmy go 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, że chcę też kolorów .
Okej, więc duża część naszego zapytania jest już napisana, ale musimy użyć trzeciej tabeli, która jest kolorami. Teraz nasza główna tabela informacyjna cars
przechowuje identyfikator koloru samochodu, który prowadzi do kolumny identyfikatora kolorów. Tak więc, podobnie jak oryginał, możemy dołączyć do trzeciego stołu:
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 pobrać rzeczywiste informacje z nowej tabeli, którą właśnie połączyliśmy.
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)
Racja, to na chwilę szef od naszych pleców. Teraz wyjaśnię to trochę bardziej szczegółowo. Jak widać, from
klauzula w naszym zestawieniu łączy naszą główną tabelę (często używam tabeli zawierającej informacje zamiast tabeli odnośników lub wymiarów. Zapytanie działałoby równie dobrze z przełączonymi tabelami, ale mniej sensowne, gdy wrócimy do tego zapytania, aby je przeczytać za kilka miesięcy, więc często najlepiej jest napisać zapytanie, które będzie miłe i łatwe do zrozumienia - ułóż je intuicyjnie, użyj ładnego wcięcia, aby wszystko było tak jasne, jak może być. Jeśli będziesz uczyć innych, spróbuj zaszczepić te cechy w ich zapytaniach - szczególnie jeśli będziesz je rozwiązywał.
W ten sposób można nadal łączyć coraz więcej tabel.
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 dołączyć tabelę, w której moglibyśmy chcieć dołączyć więcej niż jedną kolumnę do join
instrukcji, oto przykład. Jeśli models
tabela zawiera modele specyficzne dla marki, a zatem ma również kolumnę o nazwie, brand
która łączy się z brands
tabelą w ID
polu, można to zrobić w następujący 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
and b.brand=d.ID
where
b.ID=1
Widzisz, powyższe zapytanie nie tylko łączy połączone tabele z cars
tabelą główną , ale także określa połączenia między już połączonymi tabelami. Jeśli tego nie zrobiono, wynik nazywany jest łączeniem kartezjańskim - co oznacza, że dba mówi źle. Sprzężenie kartezjańskie to takie, w którym zwracane są wiersze, ponieważ informacje nie informują bazy danych, jak ograniczyć wyniki, więc zapytanie zwraca wszystkie wiersze spełniające kryteria.
Aby podać przykład sprzężenia kartezjańskiego, uruchommy 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 jeśli chodzi o bazę danych, to dokładnie o to poproszono. W zapytaniu poprosiliśmy o podanie ID
od cars
i model
od models
. Ponieważ jednak nie określiliśmy sposobu łączenia tabel, baza danych dopasowała każdy wiersz z pierwszej tabeli do każdego wiersza z drugiej tabeli.
Okej, więc szef wrócił i znowu chce więcej informacji. Chcę tę samą listę, ale także zawierać w niej 4WD .
To jednak daje nam doskonałą wymówkę, aby spojrzeć na dwa różne sposoby osiągnięcia tego celu. Możemy dodać kolejny warunek do klauzuli where:
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
Chociaż powyższe będzie działać doskonale, spójrzmy na to inaczej, jest to świetna wymówka, aby pokazać, jak działa union
zapytanie.
Wiemy, że następujące produkty zwrócą wszystkie samochody sportowe:
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
A poniższe zwróci wszystkie 4WD:
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
Dodając union all
między nimi klauzulę, 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, a następnie wyniki drugiego zapytania.
W tym przykładzie byłoby oczywiście o wiele łatwiej po prostu użyć pierwszego zapytania, ale union
zapytania mogą być świetne w określonych przypadkach. Są świetnym sposobem na zwrócenie określonych wyników z tabel z tabel, które nie dają się łatwo połączyć - lub, w tym przypadku, całkowicie niepowiązanych tabel. Należy jednak przestrzegać kilku zasad.
- Typy kolumn z pierwszego zapytania muszą pasować do typów kolumn z każdego innego zapytania poniżej.
- Nazwy kolumn z pierwszego zapytania zostaną wykorzystane do zidentyfikowania całego zestawu wyników.
- Liczba kolumn w każdym zapytaniu musi być taka sama.
Być może zastanawiasz się, jaka jest różnica między używaniem union
a union all
. union
Zapytania usunie duplikaty, póki union all
nie będzie. Oznacza to, że podczas korzystania z union
nadmiernej wydajności występuje niewielki spadek wydajności, union all
ale wyniki mogą być tego warte - nie będę jednak spekulował na ten temat.
W tej notatce warto zwrócić uwagę na kilka dodatkowych uwag.
- Jeśli chcielibyśmy zamówić wyniki, możemy użyć,
order by
ale nie możesz już używać aliasu. W powyższym zapytaniu dodanie an order by a.ID
spowoduje błąd - jeśli chodzi o wyniki, kolumna jest wywoływana ID
zamiast a.ID
- mimo że w obu zapytaniach zastosowano ten sam alias.
- Możemy mieć tylko jedno
order by
oświadczenie, które musi być ostatnim oświadczeniem.
Do kolejnych przykładów dodam kilka dodatkowych wierszy do naszych tabel.
Dodałem Holden
do tabeli marek. Dodałem również wiersz, cars
który ma color
wartość 12
- która nie ma odniesienia w tabeli kolorów.
Okej, szef znowu wrócił, szczekając prośbami - * Chcę liczbę każdej przewożonej marki i liczbę samochodów w niej! ”- Typowo, przechodzimy do interesującej części naszej dyskusji, a szef chce więcej pracy .
Rightyo, 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, gdy 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 oczywiście stanowi problem - nie widzimy wzmianki o uroczej Holden
marce, którą dodałem.
Jest tak, ponieważ złączenie szuka pasujących wierszy w obu tabelach. Ponieważ w typach samochodów nie ma danych, Holden
nie są one zwracane. Tutaj możemy użyć outer
złączenia. Spowoduje to zwrócenie wszystkich wyników z jednej tabeli, niezależnie od tego, czy zostaną 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, gdy to mamy, możemy dodać uroczą funkcję agregującą, aby policzyć i na chwilę odrzucić bossa.
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)
A wraz z tym szef odchodzi.
Aby wyjaśnić to bardziej szczegółowo, sprzężenia zewnętrzne mogą być typu left
lub right
. Lewy lub prawy określa, która tabela jest w pełni uwzględniona. A left outer join
obejmie wszystkie wiersze z tabeli po lewej stronie, podczas gdy (zgadłeś) a a right outer join
przenosi wszystkie wyniki z tabeli po prawej stronie do wyników.
Niektóre bazy danych pozwolą na full outer join
przywrócenie wyników (dopasowanych lub nie) z obu tabel, ale nie jest to obsługiwane we wszystkich bazach danych.
Prawdopodobnie w tym momencie myślę, że zastanawiasz się, czy możesz scalić typy złączeń w zapytaniu - a odpowiedź brzmi tak, absolutnie możesz.
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)
Dlaczego więc nie są to oczekiwane wyniki? Dzieje się tak, ponieważ chociaż wybraliśmy łączenie zewnętrzne od samochodów do marek, nie zostało ono określone w łączeniu z kolorami - więc to konkretne połączenie przyniesie tylko wyniki, które pasują do obu tabel.
Oto zapytanie, które zadziałałoby, 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ć, w zapytaniu mamy dwa sprzężenia zewnętrzne, a wyniki są zgodne z oczekiwaniami.
A co powiesz na inne rodzaje złączeń, o które pytasz? Co z skrzyżowaniami?
Cóż, nie wszystkie bazy danych obsługują, intersection
ale prawie wszystkie bazy danych pozwolą ci stworzyć skrzyżowanie poprzez sprzężenie (lub dobrze skonstruowaną instrukcję co najmniej).
Przecięcie jest rodzajem łączenia nieco podobnym do union
opisanego powyżej - ale różnica polega na tym, że zwraca tylko wiersze danych, które są identyczne (i mam na myśli identyczne) między różnymi pojedynczymi zapytaniami połączonymi przez związek. Zwrócone zostaną tylko wiersze identyczne pod każdym względem.
Prostym przykładem byłby taki:
select
*
from
colors
where
ID>2
intersect
select
*
from
colors
where
id<4
Podczas gdy normalne union
zapytanie zwróci wszystkie wiersze tabeli (pierwsze zapytanie zwraca wszystko, ID>2
a drugie ma coś ID<4
), co da pełny zestaw, zapytanie przecinające zwróci tylko dopasowanie wiersza, id=3
ponieważ spełnia oba kryteria.
Teraz, jeśli twoja baza danych nie obsługuje intersect
zapytania, powyższe można łatwo osiągnąć za pomocą następującego zapytania:
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 między dwoma różnymi tabelami przy użyciu bazy danych, która z natury nie obsługuje zapytania o przecięcie, musisz utworzyć złączenie w każdej kolumnie tabeli.
Ok, uważam 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 czuć, że się mylę, mogą w 101% edytować i krytykować moją odpowiedź. ( Szczerze mówiąc, czuję się bardzo wdzięczny za sprostowanie mój błąd (s). )
Zamieszczę niektóre często zadawane pytania w
MySQL
tagu.Sztuczka nr 1 ( wiersze pasujące do wielu warunków )
Biorąc pod uwagę ten schemat
PYTANIE
Znajdź wszystkie filmy należące przynajmniej do obu kategorii
Comedy
iRomance
kategorii.Rozwiązanie
To pytanie może być czasami bardzo trudne. Może się wydawać, że takie zapytanie będzie odpowiedzią:
Demo SQLFiddle
co jest zdecydowanie bardzo błędne, ponieważ nie daje żadnego rezultatu . Wyjaśnienie tego jest takie, że
CategoryName
w każdym wierszu jest tylko jedna poprawna wartość . Na przykład pierwszy warunek zwraca wartość true , drugi warunek jest zawsze fałszywy. Tak więc, używającAND
operatora oba warunki powinny być spełnione; w przeciwnym razie będzie to fałsz. Kolejne zapytanie jest takie,Demo SQLFiddle
a wynik jest nadal niepoprawny, ponieważ pasuje do rekordu, który ma co najmniej jedno dopasowanie w
categoryName
. Prawdziwym rozwiązaniem byłoby przez zliczenie liczby wystąpień na rekordowo filmu . Liczba instancji powinna być zgodna z całkowitą liczbą wartości podanych w warunku.SQLFiddle Demo (odpowiedź)
Sztuczka nr 2 ( maksymalny rekord dla każdego wpisu )
Biorąc pod uwagę schemat
PYTANIE
Znajdź najnowszą wersję każdego oprogramowania. Wyświetlane są następujące kolumny:
SoftwareName
,Descriptions
,LatestVersion
( z kolumny VersionNo )DateReleased
Rozwiązanie
Niektórzy programiści SQL błędnie używają
MAX()
funkcji agregujących. Zwykle tworzą tak,Demo SQLFiddle
( większość RDBMS generuje błąd składniowy z tego powodu, że nie określono niektórych niezagregowanych kolumn w
group by
klauzuli ) wynik daje prawidłoweLatestVersion
w każdym oprogramowaniu, ale oczywiścieDateReleased
są one nieprawidłowe.MySQL
nie obsługuje,Window Functions
aCommon Table Expression
jak już niektóre RDBMS już. Obejściem tego problemu jest utworzenie wartości,subquery
która pobiera indywidualne maksimumversionNo
dla każdego oprogramowania, a następnie łączy się z innymi tabelami.SQLFiddle Demo (odpowiedź)
Tak to było. Będę publikować kolejne, gdy tylko przypomnę sobie inne FAQ na temat
MySQL
tagu. Dziękujemy za przeczytanie tego małego artykułu. Mam nadzieję, że masz przynajmniej trochę wiedzy na ten temat.AKTUALIZACJA 1
Sztuczka nr 3 ( Znajdowanie najnowszego rekordu między dwoma identyfikatorami )
Biorąc pod uwagę schemat
PYTANIE
Znajdź najnowszą rozmowę między dwoma użytkownikami.
Rozwiązanie
Demo SQLFiddle
źródło
comedy
iromance
.Having
nie pasuje to ...distinct
klauzulę mającą SQLFiddle Demo : DCzęść 2 - podzapytania
Okej, teraz szef znowu się włamał - chcę listę wszystkich naszych samochodów z marką i razem ile ich mamy!
To świetna okazja, aby użyć następnej sztuczki w naszej torbie dodatków SQL - podzapytania. Jeśli nie znasz tego terminu, podzapytanie to zapytanie uruchamiane w innym zapytaniu. Istnieje wiele różnych sposobów ich wykorzystania.
Na nasze życzenie, najpierw zestawmy proste zapytanie, które będzie zawierało listę każdego samochodu i marki:
Teraz, jeśli chcielibyśmy po prostu uzyskać liczbę samochodów posortowanych według marki, moglibyśmy oczywiście napisać to:
Więc powinniśmy móc po prostu dodać funkcję count do naszego oryginalnego zapytania, prawda?
Niestety nie możemy tego zrobić. Powodem jest to, że kiedy dodajemy identyfikator samochodu (kolumna a.ID), musimy dodać go do grupy do - więc teraz, gdy działa funkcja liczenia, istnieje tylko jeden identyfikator dopasowany do identyfikatora.
Tutaj możemy jednak użyć podkwerendy - w rzeczywistości możemy wykonać dwa całkowicie różne typy podkwerendy, które zwrócą te same wyniki, których potrzebujemy do tego. Pierwszym z nich jest po prostu umieszczenie podzapytania w
select
klauzuli. Oznacza to, że za każdym razem, gdy otrzymujemy wiersz danych, podkwerenda zostanie uruchomiona, zdobędzie kolumnę danych, a następnie wstawi ją do naszego wiersza danych.I Bam !, to by nam zrobiło. Jeśli jednak zauważyłeś, to zapytanie będzie musiało być uruchomione dla każdego zwracanego wiersza danych. Nawet w tym małym przykładzie mamy tylko pięć różnych marek samochodów, ale podzapytanie przebiegało jedenaście razy, ponieważ mamy jedenaście wierszy danych, które zwracamy. Tak więc w tym przypadku nie wydaje się to najbardziej wydajnym sposobem pisania kodu.
Dla innego podejścia, uruchommy podkwerendę i udawajmy, że jest to tabela:
Ok, więc mamy te same wyniki (uporządkowane nieco inaczej - wygląda na to, że baza danych chciała zwrócić wyniki uporządkowane według pierwszej kolumny, którą wybraliśmy tym razem) - ale te same poprawne liczby.
Jaka jest różnica między nimi - i kiedy powinniśmy używać każdego typu podzapytania? Po pierwsze, upewnijmy się, że rozumiemy, jak działa to drugie zapytanie. W
from
klauzuli naszego zapytania wybraliśmy dwie tabele , 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 mogą być pewne korzyści z korzystania z tej metody (jak również pewne ograniczenia). Najważniejsze jest to, że to podzapytanie prowadziło wiersze danych, jeśli będziemy używać prostego łączenia, jak w powyższym zapytaniu. Jeśli sobie przypomnisz, łączenie spowoduje wycofanie tylko wierszy, które mają pasujące dane po obu stronach złączenia. Jeśli nie będziemy ostrożni, może to spowodować, że prawidłowe dane nie zostaną zwrócone z tabeli naszych samochodów, jeśli w tym podzapytaniu nie będzie pasującego wiersza. raz . Gdyby nasza baza danych zawierała dużą ilość danych, może być ogromna poprawa w stosunku do pierwszej metody. Ponieważ jednak używamy tego jako tabeli, musimy wprowadzić dodatkowe wiersze danych - aby mogły one zostać ponownie połączone z naszymi wierszami danych. Musimy również mieć pewność, że jest ich wystarczająco dużoTeraz, patrząc wstecz na pierwsze podzapytanie, istnieją również pewne ograniczenia. ponieważ ściągamy dane z powrotem do jednego wiersza, możemy wycofać TYLKO jeden wiersz danych. Podzapytania używane w
select
klauzuli kwerendy bardzo często używać tylko funkcji, takich jak kruszywasum
,count
,max
lub innej podobnej funkcji zbiorczej. Nie muszą , ale często tak się pisze.Zanim przejdziemy dalej, rzućmy okiem, gdzie jeszcze możemy użyć podzapytania. Możemy użyć tego w
where
klauzuli - teraz ten przykład jest nieco spreparowany, ponieważ w naszej bazie danych są lepsze sposoby na uzyskanie następujących danych, ale ponieważ jest to tylko przykład, spójrzmy:To zwraca nam listę identyfikatorów marek i nazw marek (druga kolumna została dodana tylko w celu pokazania marek), które zawierają literę
o
w nazwie.Teraz możemy użyć wyników tego zapytania w klauzuli where to:
Jak widać, nawet jeśli podzapytanie zwracało trzy identyfikatory marek, w naszej tabeli samochodów były wpisy tylko dla dwóch z nich.
W tym przypadku, dla dalszych szczegółów, podzapytanie działa tak, jakbyśmy napisali następujący kod:
Ponownie można zobaczyć, w jaki sposób podzapytanie w porównaniu z ręcznymi danymi wejściowymi zmieniło kolejność wierszy podczas powrotu z bazy danych.
Podczas omawiania podkwerend, zobaczmy, co jeszcze możemy zrobić z podkwerendami:
select
klauzuli, niektóre wfrom
klauzuli i kilka innych wwhere
klauzuli - pamiętaj tylko, że każde z nich powoduje, że zapytanie jest bardziej złożone i może potrwać dłużej wykonać.Jeśli potrzebujesz napisać jakiś wydajny kod, może być korzystne napisanie zapytania na kilka sposobów i sprawdzenie (przez czas lub za pomocą planu wyjaśniania), które jest optymalnym zapytaniem, aby uzyskać wyniki. Pierwszy sposób, który działa, nie zawsze może być najlepszym sposobem.
źródło
Część 3 - Sztuczki i skuteczny kod
Wydajność MySQL w ()
Pomyślałem, że dodam kilka dodatkowych bitów, aby znaleźć wskazówki i triki.
Jedno pytanie, które, jak się wydaje, pojawiło się dość często , brzmi: Jak uzyskać niepasujące wiersze z dwóch tabel i widzę odpowiedź najczęściej akceptowaną jako coś w tym rodzaju (na podstawie tabeli naszych samochodów i marek - na której Holden wymieniono jako marka, ale nie pojawia się w tabeli samochodów):
I tak to zadziała.
Jednak to nie skuteczny w niektórych danych. Oto link do pytania o przepełnienie stosu z pytaniem o to, a tutaj jest artykuł o doskonałej szczegółowości, jeśli chcesz dostać się w szczegóły.
Krótka odpowiedź brzmi: jeśli optymalizator nie poradzi sobie z nią skutecznie, może być znacznie lepiej użyć zapytania takiego jak poniżej, aby uzyskać niepasujące wiersze:
Zaktualizuj tabelę z tą samą tabelą w podzapytaniu
Ahhh, kolejny oldie ale goodie - stary Nie można określić „marek” tabeli docelowej do aktualizacji w klauzuli FROM .
MySQL nie pozwoli ci uruchomić
update...
zapytania z podselekcją w tej samej tabeli. Być może zastanawiasz się, dlaczego po prostu nie wpaść w klauzulę „gdzie”, prawda? Ale co, jeśli chcesz zaktualizować tylko wiersz zmax()
datą między wieloma innymi wierszami? Nie można tego dokładnie zrobić w klauzuli where.Więc nie możemy tego zrobić? Nie do końca. Istnieje zaskakujące obejście, o którym zaskakująco duża liczba użytkowników nie wie - chociaż zawiera pewne hakery, na które trzeba zwrócić uwagę.
Możesz umieścić podkwerendę w innym podkwerendie, co spowoduje dostateczną przerwę między dwoma zapytaniami, aby działało. Należy jednak pamiętać, że najbezpieczniej jest trzymać zapytanie w transakcji - zapobiegnie to wszelkim innym zmianom w tabelach podczas działania zapytania.
źródło
Można użyć koncepcji wielu zapytań w słowie kluczowym FROM. Pokażę ci jeden przykład:
Możesz użyć tyle tabel, ile chcesz. Używaj złączeń zewnętrznych i łączeń tam, gdzie jest to konieczne, nawet wewnątrz podkwerend tabel.
Jest to bardzo łatwa metoda angażowania tylu tabel i pól.
źródło
Mam nadzieję, że dzięki temu znajdziesz tabele podczas czytania:
jsfiddle
źródło