web-dev-qa-db-fra.com

Requête SQL renvoyer des données de plusieurs tables

J'aimerais savoir ce qui suit:

  • comment obtenir des données de plusieurs tables dans ma base de données?
  • quels types de méthodes existe-t-il pour le faire?
  • que sont les unions et les syndicats et en quoi sont-ils différents les uns des autres?
  • Quand devrais-je utiliser chacun par rapport aux autres?

Je prévois de l'utiliser dans mon application (par exemple, PHP), mais je ne souhaite pas exécuter plusieurs requêtes sur la base de données. Quelles sont les options pour obtenir les données de plusieurs tables en une seule requête?

Note: J'écris ceci car je voudrais pouvoir créer un lien vers un guide bien écrit sur les nombreuses questions que je rencontre constamment dans la file d'attente PHP. Je poste une réponse.

Les réponses couvrent les points suivants:

  1. Partie 1 - Jointures et unions
  2. Partie 2 - Sous-requêtes
  3. Partie 3 - Astuces et code efficace
  4. Partie 4 - Sous-requêtes dans la clause From
  5. Partie 5 - Sac mélangé d'astuces de John
420
Fluffeh

Partie 1 - Jointures et unions

Cette réponse couvre:

  1. Partie 1
    • Joindre deux ou plusieurs tables en utilisant une jointure interne (voir le entrée wikipedia pour plus d'informations)
    • Comment utiliser une requête d'union
    • Jointures externes gauches et droites (this stackOverflow answer est excellent pour décrire les types de jointure)
    • Requêtes intersectes (et comment les reproduire si votre base de données ne les prend pas en charge) - ceci est une fonction de SQL-Server ( voir info ) et une partie de la raison pour laquelle j'ai écrit tout ce projet chose en premier lieu.
  2. Partie 2
    • Sous-requêtes - ce qu’elles sont, où elles peuvent être utilisées et ce qu’il faut surveiller
    • Cartésien rejoint AKA - Oh, la misère!

Il existe plusieurs méthodes pour extraire des données de plusieurs tables d'une base de données. Dans cette réponse, j'utiliserai la syntaxe de jointure ANSI-92. Cela peut être différent de plusieurs autres tutoriels utilisant l'ancienne syntaxe ANSI-89 (et si vous en avez l'habitude, cela peut sembler beaucoup moins intuitif - mais tout ce que je peux dire, c'est d'essayer) tel quel beaucoup plus facile à comprendre lorsque les requêtes commencent à devenir plus complexes. Pourquoi l'utiliser? Y a-t-il un gain de performance? 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 dispose d'une base de données pour garder une trace des voitures disponibles. Le propriétaire vous a engagé comme informaticien et s'attend à ce que vous soyez en mesure de lui transmettre les données qu'il demande en un rien de temps.

J'ai créé un certain nombre de tables de consultation qui seront utilisées par la table finale. Cela nous donnera un modèle de travail raisonnable. Pour commencer, je vais exécuter mes requêtes sur un exemple de base de données ayant la structure suivante. Je vais essayer de réfléchir aux erreurs courantes commises lors du démarrage et d’expliquer ce qui ne va pas avec elles - et bien sûr de montrer comment les corriger.

La première table est simplement une liste de couleurs afin que nous sachions quelles couleurs nous avons dans le garage.

_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 constructeur 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 modèle de table couvrira différents types de voitures, il sera plus simple d'utiliser différents types de voitures 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 attacher toutes ces autres tables, la table qui relie tout. Le champ ID est en réalité 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, ainsi que suffisamment de données pour les rendre rentables.

Le patron veut donc connaître les identifiants de toutes ses voitures de sport .

Ceci est une simple jointure à deux tables. Nous avons une table qui identifie le modèle et la table avec le stock disponible qu'il contient. Comme vous pouvez le constater, les données de la colonne model de la table cars se rapportent à la colonne models de la table cars que nous avons. Nous savons maintenant que la table de modèles a un ID de _1_ pour Sports, nous allons donc écrire la jointure.

_select
    ID,
    model
from
    cars
        join models
            on model=ID
_

Donc, cette requête a l'air bien non? Nous avons identifié les deux tables et contient les informations nécessaires. Nous utilisons une jointure qui identifie correctement les colonnes à 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 effectivement les bonnes colonnes, mais certaines d'entre elles existent dans les deux tables, de sorte que la base de données devient confuse quant à la colonne réelle que nous voulons dire et où. Il existe deux solutions pour résoudre ce problème. La première 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 s'appelle aliasing de table. Les tables de cet exemple portent des noms simples et courts, mais saisir quelque chose comme _KPI_DAILY_SALES_BY_DEPARTMENT_ vieillirait probablement rapidement. Un moyen simple consiste donc à surnommer la table de la manière suivante:

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

Revenons maintenant à la demande. Comme vous pouvez le constater, nous disposons des informations dont nous avons besoin, mais nous avons également des informations qui ne nous ont pas été demandées. Nous devons donc inclure une clause where dans la déclaration afin de ne récupérer que les voitures de sport demandées. Comme je préfère la méthode des alias de table plutôt que d’utiliser les noms de table encore et encore, je vais m'en tenir à ça à partir de maintenant.

Clairement, 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 que vous tapez moins), utilisons-le 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 ne jamais être satisfait de ce qu'il a demandé, il regarde les informations, puis dit Je veux aussi les couleurs .

Bon, nous avons déjà écrit une bonne partie de notre requête, mais nous devons utiliser un troisième tableau, celui des couleurs. Maintenant, notre table principale d'informations cars enregistre l'ID de couleur de la voiture et 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)
_

Bon sang, bien que la table ait été correctement jointe et que les colonnes associées soient liées, nous avons oublié d’extraire les informations réelles de la nouvelle table que nous venons de créer un lien.

_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)
_

Bien, c'est le patron de notre dos pour un moment. Maintenant, pour expliquer certaines de ces choses un peu plus en détail. Comme vous pouvez le constater, la clause from de notre instruction lie notre table principale (j’utilise souvent une table contenant des informations plutôt qu’une table de recherche ou de dimension. La requête fonctionnerait tout aussi bien avec les tables basculées, mais elles auraient 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 soit agréable et facile à comprendre - présentez-la de manière intuitive, utilisez la mise en retrait de Nice pour que tout soit aussi clair Si vous continuez à enseigner aux autres, essayez d’incorporer ces caractéristiques à leurs requêtes - en particulier si vous comptez les résoudre.

Il est tout à fait possible de continuer à relier 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'ai oublié d'inclure une table dans laquelle nous pourrions vouloir joindre plus d'une colonne dans l'instruction join, voici un exemple. Si la table models avait des modèles propres à la marque et donc également une colonne appelée brand qui renvoyait à la table brands du champ ID, cela pourrait se faire 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
_

Comme vous pouvez le constater, la requête ci-dessus lie non seulement les tables jointes à la table principale cars, mais spécifie également des jointures entre les tables déjà jointes. Si cela n'a pas été fait, le résultat s'appelle une jointure cartésienne - ce qui est dba parler pour le mal. Une jointure cartésienne est une jointure dans laquelle les lignes sont renvoyées car les informations n'indiquant pas à la base de données comment limiter les résultats, la requête renvoie all toutes les lignes répondre aux critères.

Donc, pour donner un exemple de jointure cartésienne, lançons 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é ID from cars et model from models. Cependant, comme nous n’avons pas précisé comment joindre les tables, la base de données a correspondu chaque ligne de la première table avec toutes les lignes de la deuxième table.

Bon, le patron est de retour et il veut plus d'informations. Je veux la même liste, mais j'y inclut également les 4WD .

Cependant, cela nous donne une bonne excuse pour examiner deux manières 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 fonctionne parfaitement, voyons cela différemment, c’est une excellente excuse pour montrer comment une requête union fonctionnera.

Nous savons que ce qui suit retournera 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 les suivants retourneraient tous les 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
_

Donc, en ajoutant une clause _union all_ entre eux, les résultats de la seconde 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 constater, les résultats de la première requête sont renvoyés en premier, suivis des résultats de la seconde requête.

Dans cet exemple, il aurait bien sûr été beaucoup plus simple d’utiliser la première requête, mais les requêtes union peuvent s'avérer très utiles dans certains cas. Ils constituent un excellent moyen de renvoyer des résultats spécifiques à partir de tables provenant de tables qui ne sont pas facilement jointes - ou d'ailleurs complètement des tables non liées. Il y a quelques règles à suivre cependant.

  • Les types de colonne de la première requête doivent correspondre aux types de colonne 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 identique.

Maintenant, vous pourriez vous demander quelle est la différence entre utiliser union et _union all_. Une requête union supprimera les doublons, alors que _union all_ ne le fera pas. Cela signifie qu'il y a une petite baisse de performance lors de l'utilisation de union sur _union all_ mais les résultats peuvent en valoir la peine - je ne spéculerai pas sur ce genre de chose dans ce cas-ci.

Sur cette note, il convient de noter quelques notes supplémentaires ici.

  • Si nous voulions commander 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 s'appelle ID et non _a.ID_ - même si le même alias a été utilisé dans les deux requêtes. .
  • Nous ne pouvons avoir qu'une seule instruction _order by_, et il doit s'agir de la dernière instruction.

Pour les exemples suivants, j'ajoute quelques lignes supplémentaires à nos tables.

J'ai ajouté Holden à la table des marques. J'ai également ajouté une ligne dans cars qui a la valeur color de _12_ - qui n'a pas de référence dans la table des couleurs.

D'accord, le patron est de retour et aboie des requêtes - * Je veux un décompte de chaque marque que nous vendons et du nombre de voitures qu'il contient! `- Typiques, nous arrivons à une partie intéressante de notre discussion et le patron veut plus de travail .

Rightyo, la première chose à faire est donc 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 ceci à notre table des 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 marque Holden 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 dans les voitures de type Holden, elles ne sont pas renvoyées. C'est ici que nous pouvons utiliser une jointure outer. Ceci renverra tous les résultats d'une table, qu'ils soient appariés 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égat pour obtenir un décompte et libérer le patron de notre dos un instant.

_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, éloignez le patron.

Maintenant, pour expliquer cela plus en détail, les jointures externes peuvent être du type left ou right. La gauche ou la droite définit quelle table est totalement incluse. Un _left outer join_ inclura toutes les lignes du tableau de gauche, alors que (vous l’aurez deviné) un _right outer join_ amène tous les résultats du tableau de droite dans les résultats.

Certaines bases de données autoriseront un _full outer join_ qui rapportera les résultats (correspondants ou non) de des deux tables , mais cela n'est pas pris en charge dans toutes les bases de données.

Maintenant, je suppose que, à ce stade, vous vous demandez si vous pouvez ou non fusionner des 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 ne s'agit-il pas des résultats escomptés? En effet, bien que nous ayons sélectionné la jointure externe des voitures aux marques, cela n’a pas été spécifié dans la jointure aux couleurs - de sorte que cette jointure particulière ne ramène que les résultats correspondants dans les deux tableaux.

Voici la requête qui fonctionnerait pour obtenir les résultats escomptés:

_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 constater, la requête contient deux jointures externes et les résultats sont obtenus comme prévu.

Maintenant, qu'en est-il des autres types de jointures que vous demandez? Qu'en est-il des intersections?

Eh bien, toutes les bases de données ne supportent pas intersection mais quasiment toutes les bases de données vous permettront de créer une intersection via une jointure (ou au moins une instruction where bien structurée).

Une intersection est un type de jointure similaire à un union comme décrit ci-dessus - mais la différence est qu’elle uniquement renvoie des lignes de données identiques (et Je veux dire identique) entre les différentes requêtes individuelles auxquelles le syndicat a adhéré. Seules les lignes identiques à tous égards seront retournées.

Un exemple simple serait comme tel:

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

Alors qu'une requête normale union renverrait toutes les lignes de la table (la première requête renvoyant tout ce qui dépassait _ID>2_ et la seconde tout ayant _ID<4_) qui aboutirait à un ensemble complet, une requête intersecte ne renverrait que la ligne correspondant à _id=3_ car elle répond aux deux critères.

Désormais, si votre base de données ne prend pas en charge une requête intersect, la requête ci-dessus peut être facilement complétée avec 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 sur deux tables différentes en utilisant une base de données qui ne prend pas en charge de manière inhérente une requête d'intersection, vous devez créer une jointure sur chaque colonne des tables.

451
Fluffeh

Ok, j'ai trouvé ce post très intéressant et j'aimerais partager certaines de mes connaissances sur la création d'une requête. Merci pour cela Fluffeh. Les autres personnes qui liront peut-être ceci et qui auront peut-être l'impression que je me trompe sont libres à 101% de modifier et de critiquer ma réponse. (Honnêtement, je suis très reconnaissant d'avoir corrigé mes erreurs.)

Je posterai certaines des questions fréquemment posées dans la balise MySQL.


Astuce n ° 1 (lignes correspondant à plusieurs conditions)

Étant donné ce schéma

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

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

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

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

QUESTION

Trouvez tous les films appartenant à au moins les deuxComedy et Romance 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: -

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

Démo SQLFiddle

ce qui est vraiment très faux car cela produit pas de résultat. L'explication de ceci est qu'il n'y a qu'une seule valeur valide de CategoryName sur chaque ligne. Par exemple, la première condition renvoie true, la seconde condition est toujours fausse. Ainsi, en utilisant l'opérateur AND, les deux conditions doivent être vraies; sinon, ce sera faux. Une autre requête est comme ça,

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

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 solution réelle serait en comptant le nombre d'instances d'enregistrement par film. Le nombre d'instances doit correspondre au nombre total de valeurs fournies dans la condition.

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

Démo SQLFiddle (la réponse)


Astuce n ° 2 (enregistrement maximum pour chaque entrée)

Schéma donné,

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

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

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

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

QUESTION

Trouvez la dernière version sur chaque logiciel. Affichez les colonnes suivantes: SoftwareName, Descriptions, LatestVersion (de la colonne VersionNo), DateReleased

Solution

Certains développeurs SQL utilisent par erreur MAX() fonction d'agrégat. Ils ont tendance à créer comme ça,

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

Démo SQLFiddle

(la plupart des SGBDR génèrent une erreur de syntaxe car ils ne spécifient pas certaines des colonnes non agrégées de la clause group by) le résultat produit le LatestVersion correct sur chaque logiciel, mais évidemment, les DateReleased sont incorrects. MySQL ne prend pas _ en charge Window Functions et Common Table Expression comme certains RDBMS le font déjà. La solution de contournement à ce problème consiste à créer un subquery qui obtient le maximum individuel versionNo sur chaque logiciel, puis sur les autres tables.

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

Démo SQLFiddle (la réponse)


Donc c'était ça. J'en posterai un autre bientôt si je me souviens de n'importe quel autre FAQ sur la balise MySQL. Merci d'avoir lu ce petit article. J'espère que vous en aurez au moins même un peu de connaissance.

UPDATE 1


Astuce n ° 3 (Recherche du dernier enregistrement entre deux ID)

Schéma donné

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

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

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

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

QUESTION

Trouvez la dernière conversation entre deux utilisateurs.

Solution

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
)

Démo SQLFiddle

98
John Woo

Partie 2 - Sous-requêtes

Ok, maintenant le patron a de nouveau éclaté - Je veux une liste de toutes nos voitures avec la marque et le nombre total de marques de cette marque que nous avons!

C’est une excellente occasion d’utiliser la prochaine astuce de notre sac de goodies SQL: la sous-requête. Si vous ne connaissez pas le terme, une sous-requête est une requête exécutée dans une autre requête. Il y a plusieurs façons de les utiliser.

Pour notre demande, commençons par rassembler une requête simple qui listera chaque voiture et la marque:

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

Maintenant, si nous voulions simplement obtenir un nombre de voitures triées par marque, nous pourrions bien sûr écrire ceci:

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 |
+--------+-----------+

Nous devrions donc pouvoir simplement ajouter la fonction de comptage à notre requête initiale, n'est-ce pas?

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)

Malheureusement, non, nous ne pouvons pas faire cela. La raison en est que lorsque nous ajoutons l'ID de la voiture (colonne a.ID), nous devons l'ajouter au groupe - de sorte que maintenant, lorsque la fonction de comptage fonctionne, il n'y a qu'un seul ID correspondant par ID.

C’est là que nous pouvons toutefois utiliser une sous-requête - en fait, nous pouvons faire deux types de sous-requêtes complètement différents qui renverront les mêmes résultats que ceux dont nous avons besoin pour cela. La première consiste simplement à placer la sous-requête dans la clause select. Cela signifie que chaque fois que nous obtenons une ligne de données, la sous-requête s’exécutera, obtiendra une colonne de données et l’affichera dans notre ligne de données.

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)

Et Bam !, cela nous ferait l'affaire. Si vous avez remarqué, cette sous-requête devra être exécutée pour chaque ligne de données renvoyée. Même dans ce petit exemple, nous n’avons que cinq marques différentes de voiture, mais la sous-requête a été exécutée onze fois car nous avons onze rangées de données que nous retournons. 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 supposons que c'est une table:

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)

D'accord, nous avons donc les mêmes résultats (classés légèrement différents - il semble que la base de données veuille renvoyer les résultats classés par la première colonne que nous avons sélectionnée cette fois) - mais avec les mêmes bons nombres.

Alors, quelle est la différence entre les deux - et quand devrions-nous utiliser chaque type de sous-requête? Premièrement, assurons-nous de comprendre le fonctionnement de cette seconde requête. Nous avons sélectionné deux tables dans la clause from de notre requête, puis nous avons écrit une requête et indiqué à la base de données qu'il s'agissait en fait d'une table - ce dont la base de données est parfaitement satisfaite. Il peut présenter certains avantages à l’utilisation de 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 y avoir une amélioration considérable par rapport à la première méthode. Cependant, comme nous utilisons cela comme une table, nous devons importer des lignes de données supplémentaires afin qu'elles puissent être réellement reliées à nos lignes de données. Nous devons également nous assurer qu'il y a assez de lignes de données si nous allons utiliser une simple jointure comme dans la requête ci-dessus. Si vous vous en souvenez bien, la jointure ne récupérera que les lignes dont les données correspondent sur les deux côtés de la jointure. Si nous ne faisons pas attention, des données valides pourraient ne pas être renvoyées de notre table cars s'il n'y avait pas de ligne correspondante dans cette sous-requête.

Maintenant, en regardant la première sous-requête, il y a aussi quelques limitations. parce que nous récupérons les données dans une seule ligne, nous pouvons SEULEMENT récupérer une ligne de données. Les sous-requêtes utilisées dans la clause select d'une requête utilisent très souvent uniquement une fonction d'agrégat telle que sum, count, max ou une autre fonction d'agrégat similaire. Ils n'ont pas , mais c'est souvent ainsi qu'ils sont écrits.

Donc, avant de poursuivre, jetons un coup d'œil sur les endroits où nous pouvons utiliser une sous-requête. Nous pouvons l'utiliser dans la clause where - maintenant, cet exemple est un peu artificiel car dans notre base de données, il existe de meilleurs moyens d'obtenir les données suivantes, mais vu que ce n'est qu'un exemple, jetons un coup d'oeil :

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

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

Cela nous renvoie une liste d'identifiants de marques et de noms de marques (la deuxième colonne est ajoutée uniquement pour indiquer les marques) qui contiennent la lettre o dans le nom.

Maintenant, nous pourrions utiliser les résultats de cette requête dans une clause where comme ceci:

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)

Comme vous pouvez le constater, même si la sous-requête renvoyait les trois identifiants de marque, notre table cars ne contenait des entrées que pour deux d’entre elles.

Dans ce cas, pour plus de détails, la sous-requête fonctionne comme si nous avions écrit le code suivant:

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)

Encore une fois, vous pouvez voir comment une sous-requête par rapport à des entrées manuelles a modifié 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:

  • Vous pouvez placer une sous-requête dans une autre sous-requête, et ainsi de suite. Il y a une limite qui dépend de votre base de données, mais à part les fonctions récursives de certains programmeurs fous et maniaques, la plupart des gens n'atteindront jamais cette limite.
  • Vous pouvez placer plusieurs sous-requêtes dans une même requête, quelques-unes dans la clause select, d'autres dans la clause from et quelques-unes dans la clause where. vous introduisez rend votre requête plus complexe et susceptible de prendre plus de temps à être exécutée.

Si vous avez besoin d'écrire du code efficace, il peut être avantageux d'écrire la requête de plusieurs manières et de voir (en la chronométrant ou en utilisant un plan explicatif) quelle est la requête optimale pour obtenir vos résultats. Le premier moyen qui fonctionne ne constitue peut-être pas toujours le meilleur moyen de le faire.

62
Fluffeh

Partie 3 - Astuces et code efficace

MySQL en () efficacité

Je pensais ajouter quelques éléments supplémentaires, pour obtenir des conseils et astuces.

Une question que je vois assez souvent est . Comment puis-je obtenir des lignes non correspondantes de deux tables et je vois la réponse la plus communément acceptée comme quelque chose comme ci-dessous (basé sur notre tableau de voitures et marques - qui a Holden répertorié comme marque, mais n'apparaît pas dans le tableau de voitures):

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

Et oui cela fonctionnera.

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

Cependant, il est pas efficace dans certaines bases de données. Voici un lien vers une question de débordement de pile demandant à ce sujet, et voici un excellent article en profondeur si vous voulez entrer dans le vif du sujet.

En bref, si l'optimiseur ne le gère pas efficacement, il peut être préférable d'utiliser une requête semblable à celle-ci pour obtenir des lignes non appariées:

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)

Mettre à jour la table avec la même table dans la sous-requête

Ahhh, un autre vieux mais bonjour - l'ancien Vous ne pouvez pas spécifier les marques de la table cible pour la mise à jour dans la clause FROM .

MySQL ne vous autorisera pas à exécuter une requête update... avec une sous-sélection sur la même table. Maintenant, vous pensez peut-être, pourquoi ne pas simplement l'insérer dans la clause where, n'est-ce pas? Mais que se passe-t-il si vous souhaitez mettre à jour uniquement la ligne avec la date max() parmi plusieurs autres lignes? Vous ne pouvez pas faire exactement cela dans une clause where.

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

Donc, on ne peut pas faire ça hein? Eh bien, pas exactement. Il existe une solution de contournement sournoise dont un nombre étonnamment grand d'utilisateurs ne sont pas au courant - bien qu'elle inclue certains piratages auxquels vous devrez prêter attention.

Vous pouvez coller la sous-requête dans une autre sous-requête, ce qui laisse un espace suffisant 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.

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

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

Vous pouvez utiliser le concept de plusieurs requêtes dans le mot clé FROM. Laissez-moi vous montrer un exemple:

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

Vous pouvez utiliser autant de tables que vous le souhaitez. Utilisez les jointures externes et l'union chaque fois que c'est nécessaire, même à l'intérieur des sous-requêtes de table.

C'est une méthode très facile d'impliquer autant que des tables et des champs.

18
prashant1988

J'espère que cela vous permettra de trouver les tables pendant que vous lisez la chose:

jsfiddle

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