Partie 1 - Jointures et unions
Cette réponse couvre:
- Partie 1
- Joindre deux tables ou plus en utilisant une jointure interne (Voir l' entrée wikipedia pour plus d'informations)
- Comment utiliser une requête d'union
- Jointures externes gauche et droite (cette réponse stackOverflow est excellente pour décrire les types de jointures)
- Intersection des requêtes (et comment les reproduire si votre base de données ne les prend pas en charge) - c'est une fonction de SQL-Server ( voir info ) et une des raisons pour lesquelles j'ai écrit tout cela en premier lieu.
- Partie 2
- Sous-requêtes - ce qu'elles sont, où elles peuvent être utilisées et à quoi faire attention
- Cartésien rejoint AKA - Oh, la misère!
Il existe plusieurs façons d'extraire des données de plusieurs tables dans une base de données. Dans cette réponse, j'utiliserai la syntaxe de jointure ANSI-92. Cela peut être différent d'un certain nombre d'autres tutoriels qui utilisent l'ancienne syntaxe ANSI-89 (et si vous êtes habitué à 89, cela peut sembler beaucoup moins intuitif - mais tout ce que je peux dire est de l'essayer) car c'est beaucoup plus facile pour comprendre quand les requêtes commencent à devenir plus complexes. Pourquoi l'utiliser? Y a-t-il un gain de performances? La réponse courte est non, mais elle est plus facile à lire une fois que vous vous y êtes habitué. Il est plus facile de lire les requêtes écrites par d'autres personnes en utilisant cette syntaxe.
Je vais également utiliser le concept d'un petit chantier qui a une base de données pour garder une trace des voitures disponibles. Le propriétaire vous a embauché comme son informaticien et s'attend à ce que vous puissiez lui laisser les données qu'il demande en un rien de temps.
J'ai créé un certain nombre de tables de recherche qui seront utilisées par la table finale. Cela nous donnera un modèle raisonnable à partir duquel travailler. Pour commencer, je vais exécuter mes requêtes sur un exemple de base de données qui a la structure suivante. J'essaierai de penser aux erreurs courantes commises lors du démarrage et d'expliquer ce qui ne va pas avec elles - ainsi que, bien sûr, de montrer comment les corriger.
Le premier tableau est simplement une liste de couleurs afin que nous sachions quelles couleurs nous avons dans le parc automobile.
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)
Le tableau des marques identifie les différentes marques de voitures que le chantier pourrait éventuellement vendre.
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)
Le tableau des modèles couvrira différents types de voitures, il sera plus simple d'utiliser des types de voitures différents plutôt que des modèles de voitures réels.
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)
Et enfin, pour lier toutes ces autres tables, la table qui relie tout. Le champ ID est en fait le numéro de lot unique utilisé pour identifier les voitures.
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)
Cela nous donnera suffisamment de données (j'espère) pour couvrir les exemples ci-dessous de différents types de jointures et donnera également suffisamment de données pour les rendre utiles.
Donc, pour entrer dans le vif du sujet, le patron veut connaître les identifiants de toutes les voitures de sport qu'il possède .
Il s'agit d'une simple jointure à deux tables. Nous avons un tableau qui identifie le modèle et le tableau avec le stock disponible. Comme vous pouvez le voir, les données de la model
colonne du cars
tableau se rapportent à la models
colonne du cars
tableau que nous avons. Maintenant, nous savons que la table des modèles a un ID 1
pour for Sports
permet donc d'écrire la jointure.
select
ID,
model
from
cars
join models
on model=ID
Donc, cette requête semble bonne non? Nous avons identifié les deux tables et contenir les informations dont nous avons besoin et utilisons une jointure qui identifie correctement sur quelles colonnes se joindre.
ERROR 1052 (23000): Column 'ID' in field list is ambiguous
Oh non! Une erreur dans notre première requête! Oui, et c'est une prune. Vous voyez, la requête a en effet les bonnes colonnes, mais certaines d'entre elles existent dans les deux tables, donc la base de données est confuse quant à la colonne réelle que nous voulons dire et où. Il existe deux solutions pour résoudre ce problème. Le premier est agréable et simple, nous pouvons utiliser tableName.columnName
pour dire à la base de données exactement ce que nous voulons dire, comme ceci:
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)
L'autre est probablement plus souvent utilisé et est appelé alias de table. Les tables de cet exemple ont des noms simples agréables et courts, mais taper quelque chose comme KPI_DAILY_SALES_BY_DEPARTMENT
serait probablement vieux rapidement, donc un moyen simple est de surnommer la table comme ceci:
select
a.ID,
b.model
from
cars a
join models b
on a.model=b.ID
Maintenant, revenons à la demande. Comme vous pouvez le voir, nous avons les informations dont nous avons besoin, mais nous avons également des informations qui n'ont pas été demandées, nous devons donc inclure une clause where dans la déclaration pour obtenir uniquement les voitures de sport comme cela a été demandé. Comme je préfère la méthode d'alias de table plutôt que d'utiliser les noms de table encore et encore, je m'en tiendrai à partir de ce point.
De toute évidence, nous devons ajouter une clause where à notre requête. Nous pouvons identifier les voitures de sport par ID=1
ou model='Sports'
. Comme l'ID est indexé et la clé primaire (et il se trouve qu'il y a moins de frappe), utilisons cela dans notre requête.
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! Le patron est content. Bien sûr, étant un patron et n'étant jamais satisfait de ce qu'il a demandé, il regarde les informations, puis dit que je veux aussi les couleurs .
D'accord, nous avons donc une bonne partie de notre requête déjà écrite, mais nous devons utiliser un troisième tableau qui est les couleurs. Maintenant, notre tableau d'informations principal cars
stocke l'ID de couleur de la voiture et cela renvoie à la colonne ID de couleurs. Ainsi, de manière similaire à l'original, nous pouvons rejoindre une troisième table:
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)
Merde, bien que la table ait été correctement jointe et que les colonnes liées aient été liées, nous avons oublié d'extraire les informations réelles de la nouvelle table que nous venons de lier.
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)
Bon, c'est le patron de notre dos pendant un moment. Maintenant, pour en expliquer un peu plus en détail. Comme vous pouvez le voir, la from
clause de notre instruction relie notre table principale (j'utilise souvent une table qui contient des informations plutôt qu'une table de recherche ou de dimension. La requête fonctionnerait tout aussi bien avec les tables toutes inversées, mais aurait moins de sens lorsque nous revenons à cette requête pour la lire dans quelques mois, il est donc souvent préférable d'essayer d'écrire une requête qui sera agréable et facile à comprendre - disposez-la intuitivement, utilisez une indentation agréable pour que tout soit aussi clair que Si vous continuez à enseigner aux autres, essayez d'inculquer ces caractéristiques dans leurs requêtes - surtout si vous allez les dépanner.
Il est tout à fait possible de continuer à lier de plus en plus de tables de cette manière.
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
Bien que j'aie oublié d'inclure un tableau où nous pourrions vouloir joindre plus d'une colonne dans la join
déclaration, voici un exemple. Si la models
table avait des modèles spécifiques à la marque et avait donc également une colonne appelée brand
qui renvoyait à la brands
table sur le ID
terrain, cela pourrait être fait comme suit:
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
Vous pouvez voir que la requête ci-dessus relie non seulement les tables jointes à la cars
table principale , mais spécifie également les jointures entre les tables déjà jointes. Si cela n'a pas été fait, le résultat est appelé une jointure cartésienne - ce qui est mauvais pour dba. Une jointure cartésienne est celle où les lignes sont renvoyées car les informations ne disent pas à la base de données comment limiter les résultats, de sorte que la requête renvoie toutes les lignes qui correspondent aux critères.
Ainsi, pour donner un exemple de jointure cartésienne, exécutons la requête suivante:
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)
Bon Dieu, c'est moche. Cependant, en ce qui concerne la base de données, c'est exactement ce qui a été demandé. Dans la requête, nous avons demandé le ID
from cars
et le model
from models
. Cependant, comme nous n'avons pas spécifié comment joindre les tables, la base de données a mis en correspondance chaque ligne de la première table avec chaque ligne de la deuxième table.
D'accord, donc le patron est de retour et il veut encore plus d'informations. Je veux la même liste, mais j'inclus également des 4x4 .
Cependant, cela nous donne une excellente excuse pour examiner deux façons différentes d'y parvenir. Nous pourrions ajouter une autre condition à la clause where comme ceci:
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
Bien que ce qui précède fonctionnera parfaitement bien, regardons les choses différemment, c'est une excellente excuse pour montrer comment une union
requête fonctionnera.
Nous savons que ce qui suit rendra toutes les voitures de sport:
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
Et ce qui suit retournerait tous les 4x4:
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
Ainsi, en ajoutant une union all
clause entre eux, les résultats de la deuxième requête seront ajoutés aux résultats de la première requête.
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)
Comme vous pouvez le voir, les résultats de la première requête sont renvoyés en premier, suivis des résultats de la deuxième requête.
Dans cet exemple, il aurait bien sûr été beaucoup plus facile d'utiliser simplement la première requête, mais les union
requêtes peuvent être utiles pour des cas spécifiques. Ils sont un excellent moyen de renvoyer des résultats spécifiques à partir de tableaux à partir de tableaux qui ne sont pas facilement réunis - ou d'ailleurs des tableaux complètement indépendants. Il y a cependant quelques règles à suivre.
- Les types de colonnes de la première requête doivent correspondre aux types de colonnes de toutes les autres requêtes ci-dessous.
- Les noms des colonnes de la première requête seront utilisés pour identifier l'ensemble des résultats.
- Le nombre de colonnes dans chaque requête doit être le même.
Maintenant, vous vous demandez peut- être quelle est la différence entre utiliser union
et union all
. Une union
requête supprimera les doublons, union all
contrairement à une requête . Cela signifie qu'il y a un petit impact sur les performances lors de l'utilisation de union
Over union all
mais les résultats peuvent en valoir la peine - je ne spéculerai pas sur ce genre de chose cependant.
Sur cette note, il peut être utile de noter ici quelques notes supplémentaires.
- Si nous voulions ordonner les résultats, nous pouvons utiliser un
order by
mais vous ne pouvez plus utiliser l'alias. Dans la requête ci-dessus, l'ajout d'un order by a.ID
entraînerait une erreur - en ce qui concerne les résultats, la colonne est appelée ID
plutôt que a.ID
- même si le même alias a été utilisé dans les deux requêtes.
- Nous ne pouvons avoir qu'une seule
order by
déclaration, et ce doit être la dernière déclaration.
Pour les exemples suivants, j'ajoute quelques lignes supplémentaires à nos tableaux.
J'ai ajouté Holden
au tableau des marques. J'ai également ajouté une ligne cars
qui a la color
valeur 12
- qui n'a aucune référence dans le tableau des couleurs.
D'accord, le patron est de retour, aboyant des demandes - * Je veux un décompte de chaque marque que nous portons et le nombre de voitures qu'il contient! `` - Typiquement, nous arrivons à une section intéressante de notre discussion et le patron veut plus de travail .
Rightyo, donc la première chose que nous devons faire est d'obtenir une liste complète des marques possibles.
select
a.brand
from
brands a
+--------+
| brand |
+--------+
| Ford |
| Toyota |
| Nissan |
| Smart |
| BMW |
| Holden |
+--------+
6 rows in set (0.00 sec)
Maintenant, lorsque nous joignons cela à notre table de voitures, nous obtenons le résultat suivant:
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)
Ce qui est bien sûr un problème - nous ne voyons aucune mention de la belle Holden
marque que j'ai ajoutée.
En effet, une jointure recherche les lignes correspondantes dans les deux tables. Comme il n'y a pas de données de type dans les voitures, Holden
elles ne sont pas retournées. C'est là que nous pouvons utiliser une outer
jointure. Cela renverra tous les résultats d'une table, qu'ils correspondent ou non dans l'autre table:
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)
Maintenant que nous avons cela, nous pouvons ajouter une belle fonction d'agrégation pour obtenir un compte et mettre le patron de notre dos pendant un moment.
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)
Et avec ça, loin du boss.
Maintenant, pour expliquer cela plus en détail, les jointures externes peuvent être de type left
ou right
. La gauche ou la droite définit quelle table est entièrement incluse. A left outer join
inclura toutes les lignes du tableau de gauche, tandis que (vous l'avez deviné) a right outer join
apporte tous les résultats du tableau de droite dans les résultats.
Certaines bases de données autorisent un full outer join
qui ramènera les résultats (qu'ils correspondent ou non) des deux tables, mais cela n'est pas pris en charge dans toutes les bases de données.
Maintenant, je suppose qu'à ce stade, vous vous demandez si vous pouvez ou non fusionner les types de jointure dans une requête - et la réponse est oui, vous le pouvez absolument.
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)
Alors, pourquoi ce ne sont pas les résultats attendus? C'est parce que bien que nous ayons sélectionné la jointure externe des voitures aux marques, elle n'a pas été spécifiée dans la jointure aux couleurs - de sorte que la jointure particulière ne ramènera que les résultats qui correspondent dans les deux tableaux.
Voici la requête qui fonctionnerait pour obtenir les résultats attendus:
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)
Comme nous pouvons le voir, nous avons deux jointures externes dans la requête et les résultats arrivent comme prévu.
Maintenant, qu'en est-il des autres types de jointures que vous demandez? Et les intersections?
Eh bien, toutes les bases de données ne prennent pas en charge le intersection
mais presque toutes les bases de données vous permettront de créer une intersection via une jointure (ou une instruction where bien structurée au moins).
Une intersection est un type de jointure quelque peu similaire à un union
tel que décrit ci-dessus - mais la différence est qu'elle ne renvoie que des lignes de données identiques (et je veux dire identiques) entre les différentes requêtes individuelles jointes par l'union. Seules les lignes identiques à tous égards seront renvoyées.
Un exemple simple serait en tant que tel:
select
*
from
colors
where
ID>2
intersect
select
*
from
colors
where
id<4
Alors qu'une union
requête normale retournerait toutes les lignes de la table (la première requête renvoyant quoi que ce soit ID>2
et la deuxième chose ayant ID<4
) ce qui entraînerait un ensemble complet, une requête intersectée ne retournerait que la correspondance de ligne id=3
car elle répond aux deux critères.
Maintenant, si votre base de données ne prend pas en charge une intersect
requête, ce qui précède peut être facilement accompagné de la requête suivante:
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)
Si vous souhaitez effectuer une intersection entre deux tables différentes à l'aide d'une base de données qui ne prend pas en charge intrinsèquement une requête d'intersection, vous devrez créer une jointure sur chaque colonne des tables.
Ok, j'ai trouvé ce post très intéressant et je voudrais partager certaines de mes connaissances sur la création d'une requête. Merci pour ce Fluffeh . Ceux qui peuvent lire ceci et penser que je me trompe sont 101% libres de modifier et de critiquer ma réponse. ( Honnêtement, je me sens très reconnaissant d'avoir corrigé mes erreurs. )
Je posterai certaines des questions fréquemment posées dans la
MySQL
balise.Astuce n ° 1 ( lignes correspondant à plusieurs conditions )
Compte tenu de ce schéma
QUESTION
Trouver tous les films qui appartiennent à au moins deux
Comedy
etRomance
catégories.Solution
Cette question peut parfois être très délicate. Il peut sembler qu'une requête comme celle-ci sera la réponse: -
Démo SQLFiddle
ce qui est définitivement très faux car il ne produit aucun résultat . L'explication est qu'il n'y a qu'une seule valeur valide de
CategoryName
sur chaque ligne . Par exemple, la première condition renvoie vrai , la deuxième condition est toujours fausse. Ainsi, en utilisant l'AND
opérateur, les deux conditions doivent être vraies; sinon, ce sera faux. Une autre requête est comme ça,Démo SQLFiddle
et le résultat est toujours incorrect car il correspond à l'enregistrement qui a au moins une correspondance sur le
categoryName
. La vraie solution serait de compter le nombre d'instances d'enregistrement par film . Le nombre d'instances doit correspondre au nombre total de valeurs fournies dans la condition.Démo SQLFiddle (la réponse)
Astuce n ° 2 ( record maximum pour chaque entrée )
Étant donné le schéma,
QUESTION
Trouvez la dernière version sur chaque logiciel. Afficher les colonnes suivantes:
SoftwareName
,Descriptions
,LatestVersion
( de la colonne VersionNo ),DateReleased
Solution
Certains développeurs SQL utilisent par erreur
MAX()
la fonction d'agrégation. Ils ont tendance à créer comme ça,Démo SQLFiddle
(la plupart des SGBDR génèrent une erreur de syntaxe à ce sujet car ils ne spécifient pas certaines des colonnes non agrégées sur la
group by
clause ) le résultat produit le correctLatestVersion
sur chaque logiciel mais évidemment lesDateReleased
sont incorrects.MySQL
ne supporte pasWindow Functions
etCommon Table Expression
pourtant comme certains SGBDR le font déjà. La solution de contournement sur ce problème est de créer unsubquery
qui obtient le maximum individuelversionNo
sur chaque logiciel et plus tard d'être joint sur les autres tables.Démo SQLFiddle (la réponse)
C'était donc ça. J'en posterai un autre bientôt car je me souviens de toute autre FAQ sur le
MySQL
tag. Merci d'avoir lu ce petit article. J'espère que vous en avez au moins obtenu même un peu de connaissances.MISE À JOUR 1
Astuce n ° 3 ( Trouver le dernier enregistrement entre deux identifiants )
Schéma donné
QUESTION
Trouvez la dernière conversation entre deux utilisateurs.
Solution
Démo SQLFiddle
la source
comedy
etromance
.Having
ne convient pas alors ..distinct
la clause having SQLFiddle Demo : DPartie 2 - Sous-requêtes
D'accord, maintenant le patron a éclaté à nouveau - je veux une liste de toutes nos voitures avec la marque et un total de combien de cette marque nous avons!
C'est une excellente occasion d'utiliser la prochaine astuce dans notre sac de goodies SQL - la sous-requête. Si vous n'êtes pas familier avec le terme, une sous-requête est une requête qui s'exécute dans une autre requête. Il existe de nombreuses façons de les utiliser.
Pour notre demande, nous allons d'abord mettre en place une requête simple qui listera chaque voiture et la marque:
Maintenant, si nous voulions simplement obtenir un nombre de voitures triées par marque, nous pourrions bien sûr écrire ceci:
Donc, nous devrions être en mesure d'ajouter simplement la fonction de comptage à notre requête d'origine, n'est-ce pas?
Malheureusement, non, nous ne pouvons pas faire ça. La raison en est que lorsque nous ajoutons l'ID de la voiture (colonne a.ID), nous devons l'ajouter dans le groupe par - alors maintenant, lorsque la fonction de comptage fonctionne, il n'y a qu'un seul ID correspondant par ID.
C'est là que nous pouvons cependant utiliser une sous-requête - en fait, nous pouvons faire deux types de sous-requête complètement différents qui retourneront les mêmes résultats que ceux dont nous avons besoin pour cela. La première consiste à simplement mettre la sous-requête dans la
select
clause. Cela signifie que chaque fois que nous obtenons une ligne de données, la sous-requête s'exécute, obtient une colonne de données, puis l'insère dans notre ligne de données.Et Bam !, cela nous ferait du bien. Si vous l'avez remarqué, cette sous-requête devra être exécutée pour chaque ligne de données que nous retournons. Même dans ce petit exemple, nous n'avons que cinq marques de voitures différentes, mais la sous-requête a été exécutée onze fois car nous avons onze lignes de données que nous renvoyons. Donc, dans ce cas, cela ne semble pas être le moyen le plus efficace d'écrire du code.
Pour une approche différente, exécutons une sous-requête et imaginons qu'il s'agit d'une table:
D'accord, nous avons donc les mêmes résultats (ordonnés légèrement différents - il semble que la base de données voulait retourner les résultats ordonnés par la première colonne que nous avons choisie cette fois) - mais les mêmes bons chiffres.
Alors, quelle est la différence entre les deux - et quand devrions-nous utiliser chaque type de sous-requête? Tout d'abord, assurons-nous de comprendre le fonctionnement de cette deuxième requête. Nous avons sélectionné deux tables dans la
from
clause de notre requête, puis écrit une requête et dit à la base de données qu'il s'agissait en fait d'une table à la place - ce dont la base de données est parfaitement satisfaite. Il peut y avoir certains avantages à utiliser cette méthode (ainsi que certaines limitations). Tout d'abord, cette sous-requête a été exécutée une fois . Si notre base de données contenait un grand volume de données, il pourrait bien y avoir une amélioration massive par rapport à la première méthode. Cependant, comme nous l'utilisons comme table, nous devons apporter des lignes de données supplémentaires - afin qu'elles puissent réellement être jointes à nos lignes de données. Nous devons également nous assurer qu'il y a suffisammentlignes de données si nous voulons utiliser une simple jointure comme dans la requête ci-dessus. Si vous vous souvenez, la jointure ne récupérera que les lignes contenant des données correspondantes des deux côtés de la jointure. Si nous ne faisons pas attention, cela pourrait entraîner le non-retour de données valides de notre table cars s'il n'y avait pas de ligne correspondante dans cette sous-requête.Maintenant, en repensant à la première sous-requête, il y a aussi quelques limitations. parce que nous retirons données dans une seule ligne, nous pouvons seulement retirer une rangée de données. Les sous - requêtes utilisées dans la
select
clause d'une requête très souvent utiliser uniquement une fonction d' agrégation tels quesum
,count
,max
ou une autre fonction d' agrégation similaire. Ils n'ont pas à le faire , mais c'est souvent ainsi qu'ils sont écrits.Donc, avant de passer à autre chose, voyons rapidement où nous pouvons utiliser une sous-requête. Nous pouvons l'utiliser dans la
where
clause - maintenant, cet exemple est un peu artificiel comme dans notre base de données, il existe de meilleures façons d'obtenir les données suivantes, mais vu qu'il ne s'agit que d'un exemple, jetons un coup d'œil:Cela nous renvoie une liste d'ID de marque et de noms de marque (la deuxième colonne n'est ajoutée que pour nous montrer les marques) contenant la lettre
o
dans le nom.Maintenant, nous pourrions utiliser les résultats de cette requête dans une clause where:
Comme vous pouvez le voir, même si la sous-requête renvoyait les trois identifiants de marque, notre tableau des voitures ne comportait des entrées que pour deux d'entre eux.
Dans ce cas, pour plus de détails, la sous-requête fonctionne comme si nous écrivions le code suivant:
Encore une fois, vous pouvez voir comment une sous-requête par rapport aux entrées manuelles a changé l'ordre des lignes lors du retour de la base de données.
Pendant que nous discutons des sous-requêtes, voyons ce que nous pouvons faire d'autre avec une sous-requête:
select
clause, certaines dans lafrom
clause et quelques autres dans lawhere
clause - rappelez-vous simplement que chacune que vous mettez rend votre requête plus complexe et risque exécuter.Si vous avez besoin d'écrire du code efficace, il peut être avantageux d'écrire la requête de plusieurs façons et de voir (soit en la chronométrant, soit en utilisant un plan d'explication) quelle est la requête optimale pour obtenir vos résultats. La première façon de fonctionner n'est pas toujours la meilleure.
la source
Partie 3 - Astuces et code efficace
MySQL en () efficacité
Je pensais que j'ajouterais quelques bits supplémentaires, pour les trucs et astuces qui ont surgi.
Une question que je vois apparaître assez souvent est la suivante: Comment puis-je obtenir des lignes non correspondantes de deux tableaux et je vois la réponse la plus communément acceptée comme quelque chose comme la suivante (basée sur notre tableau voitures et marques - qui contient Holden comme marque, mais n'apparaît pas dans le tableau des voitures):
Et oui ça va marcher.
Cependant, il n'est pas efficace dans certaines bases de données. Voici un lien vers une question Stack Overflow qui le pose, et voici un excellent article en profondeur si vous voulez entrer dans le vif du sujet.
La réponse courte est que si l'optimiseur ne le gère pas efficacement, il peut être préférable d'utiliser une requête comme la suivante pour obtenir des lignes non correspondantes:
Mettre à jour la table avec la même table dans la sous-requête
Ahhh, un autre oldie mais goodie - l'ancien Vous ne pouvez pas spécifier de «marques» de table cible pour la mise à jour dans la clause FROM .
MySQL ne vous permettra pas d'exécuter une
update...
requête avec une sous-sélection sur la même table. Maintenant, vous pensez peut-être, pourquoi ne pas simplement le gifler dans la clause where? Mais que se passe-t-il si vous souhaitez mettre à jour uniquement la ligne avec lamax()
date parmi un tas d'autres lignes? Vous ne pouvez pas faire exactement cela dans une clause where.Donc, on ne peut pas faire ça hein? Enfin, pas exactement. Il existe une solution de contournement sournoise qu'un nombre étonnamment élevé d'utilisateurs ne connaissent pas - bien qu'elle comprenne un certain piratage auquel vous devrez faire attention.
Vous pouvez coller la sous-requête dans une autre sous-requête, ce qui crée suffisamment d'espace entre les deux requêtes pour que cela fonctionne. Cependant, notez qu'il peut être plus sûr de coller la requête dans une transaction - cela empêchera toute autre modification apportée aux tables pendant l'exécution de la requête.
la source
Vous pouvez utiliser le concept de plusieurs requêtes dans le mot clé FROM. Permettez-moi de vous montrer un exemple:
Vous pouvez utiliser autant de tableaux que vous le souhaitez. Utilisez les jointures externes et l'union partout où cela est nécessaire, même à l'intérieur des sous-requêtes de table.
C'est une méthode très simple pour impliquer autant de tables et de champs.
la source
J'espère que cela lui permet de trouver les tables pendant que vous lisez la chose:
jsfiddle
la source