Requête SQL renvoie des données à partir de plusieurs tables
j'aimerais savoir ce qui suit:
- comment obtenir des données à partir de plusieurs tables dans ma base de données?
- quels types de méthodes existe-t-il pour faire cela?
- qu'est-ce que les jointures et les syndicats et en quoi sont-ils différents les uns des autres?
- Quand dois-je utiliser chacun par rapport aux autres?
j'ai l'intention d'utiliser ceci dans Mon (par exemple - PHP) application, mais vous ne voulez pas exécuter des requêtes multiples contre la base de données, quelles options ai-je pour obtenir des données à partir de plusieurs tables dans une seule requête?
Note: j'écris ceci car je voudrais pouvoir faire un lien vers un guide bien écrit sur les nombreuses questions que je rencontre constamment dans la file D'attente PHP, donc je peux faire un lien vers ceci pour plus de détails lorsque je poste une réponse.
Les réponses couvrir ce qui suit:
- Partie 1-les adhésions et les syndicats
- Partie 2 - Les Sous-Requêtes
- Partie 3 - Trucs et Efficace du Code
- Partie 4 - les sous-requêtes dans la Clause from
- de la Partie 5 - Sac Mélangé de Jean Astuces
6 réponses
Partie 1-les adhésions et les syndicats
cette réponse couvre:
- Partie 1
- joindre deux ou plusieurs tables en utilisant une jointure interne (voir la wikipedia entry for additional info)
- comment utiliser une requête de l'union
- joints extérieurs de gauche et de droite (ce réponse de débordement des piles est excellent pour décrire les types de joints)
- Intersect requêtes (et comment les reproduire si votre base de données ne les supporte pas) - c'est une fonction de SQL-Server ( voir info ) et une partie de la raison pour laquelle j'ai écrit tout cela en premier lieu.
- partie 2
- sous-séries-ce qu'elles sont, où elles peuvent être utilisées et ce qu'il faut faire attention
- jointures cartésiennes AKA-Oh, la misère!
il existe plusieurs façons d'extraire des données de plusieurs tableaux d'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 là-bas qui utilisent l'ancienne syntaxe ANSI-89 (et si vous êtes utilisé à 89, peut sembler beaucoup moins intuitif - mais tout ce que je peux dire est de l'essayer) car il est beaucoup plus facile à comprendre lorsque les requêtes commence à devenir plus complexe. Pourquoi l'utiliser? Y a-t-il un gain de performance? Le réponse courte est non, mais il est plus facile à lire une fois que vous vous y habituez. Il est plus facile de lire des requêtes écrites par d'autres personnes en utilisant cette syntaxe.
je vais également utiliser le concept d'une petite cour de triage qui dispose d'une base de données pour garder une trace des voitures disponibles. Le propriétaire vous a engagé comme son informaticien informatique et s'attend à ce que vous soyez en mesure de renoncer à lui les données qu'il demande à la baisse d'un chapeau.
j'ai fait 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 lancer mes requêtes contre une base de données d'exemple qui a la structure suivante. Je vais essayer de penser aux erreurs courantes qui sont faites au début et expliquer ce qui va mal avec eux - ainsi que, bien sûr, montrer comment les corriger.
la première table est simplement une liste de couleurs afin que nous sachions quelles couleurs nous avons dans la Cour de la voiture.
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 des voitures cour de triage 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 va être plus simple pour cela 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, attachez toutes les autres tables, la table qui lie tout ensemble. 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 assez de données (je l'espère) pour couvrir les exemples ci-dessous de différents types de jointures et aussi donner assez de données pour les rendre utiles.
Afin d'entrer dans le sable, le patron veut savoir les Id de toutes Les voitures de sport qu'il a .
il s'agit d'une simple jointure de 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 colonne model
de la table cars
se rapportent à la colonne models
de la table cars
que nous avons. Maintenant, nous savons que la table models A UN ID de 1
pour Sports
donc permet d'écrire la jointure.
select
ID,
model
from
cars
join models
on model=ID
donc cette requête semble bonne, n'est-ce pas? Nous avons identifiez les deux tables et contiennent l'information dont nous avons besoin et utilisez une jointure qui identifie correctement quelles colonnes joindre sur.
ERROR 1052 (23000): Column 'ID' in field list is ambiguous
Oh noes! 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 sur ce que la colonne réelle nous voulons dire et où. Il y a deux solutions pour résoudre ce problème. La première est simple et agréable, nous pouvons utiliser tableName.columnName
pour dites à 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 le plus souvent utilisé et est appelé aliasing de table. Les tables dans cet exemple ont des noms simples et courts, mais taper quelque chose comme KPI_DAILY_SALES_BY_DEPARTMENT
serait probablement vieillir rapidement, donc une manière simple est de donner un surnom à la table comme ceci:
select
a.ID,
b.model
from
cars a
join models b
on a.model=b.ID
maintenant, retour à la demande. Comme vous pouvez le voir, nous avons l'information que nous avons besoin, mais nous avons aussi informations qui n'ont pas été demandées, nous devons donc inclure une clause où dans la déclaration pour obtenir seulement les voitures de sport comme demandé. Comme je préfère la méthode table alias plutôt que d'utiliser les noms de table encore et encore, je vais m'y tenir à partir de ce point.
clairement, nous devons ajouter une clause où à 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 arrive à faire moins de frappe), permet d'utiliser que 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 heureux. Bien sûr, être un patron et de ne jamais être heureux avec ce qu'il a demandé, il regarde les informations, puis dit je veux des couleurs .
d'Accord, alors nous avons une bonne partie de notre requête déjà écrit, mais nous avons besoin d'une troisième table qui est en couleurs. Maintenant, notre principal tableau d'information cars
stocke L'ID de couleur de la voiture et ce lien retour aux couleurs Colonne ID. 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)
Zut, bien que la table ait été correctement jointe et que les colonnes correspondantes aient été reliées, nous avons oublié de tirer le information de la nouvelle table que nous venons de relier.
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)
C'est ça, c'est le patron sur notre dos pour un moment. Maintenant, pour expliquer un peu plus en détail. Comme vous pouvez le voir, le from
clause dans notre Énoncé lie 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 aussi bien avec les tables toutes interverties autour, mais faire moins de sens quand nous revenons à cette requête pour la lire dans quelques mois de temps, donc il est souvent préférable d'essayer d'écrire une requête qui sera agréable et facile à comprendre - l'exposer intuitivement, utiliser une belle indentation afin que tout soit aussi clair que cela peut l'être. Si vous allez sur enseignez aux autres, essayez d'instiller ces caractéristiques dans leurs requêtes - surtout si vous allez les dépanner.
il est tout à fait possible de relier de plus en plus de tableaux 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
Alors que j'ai oublié d'inclure une table où nous pourrions adhérer à plus d'une colonne dans le join
, voici un exemple. Si la table models
avait des modèles spécifiques à la marque et donc avait aussi une colonne appelée brand
qui renvoie à la table brands
sur le champ ID
, il pourrait être fait 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
and b.brand=d.ID
where
b.ID=1
vous pouvez voir, la requête au-dessus non seulement lie les tables jointes à la table principale cars
, mais spécifie aussi joins entre les tables déjà jointes. Si cela n'a pas été fait, le résultat est appelé une jointure cartésienne - qui est DBA parler pour le mauvais. Une jointure cartésienne est une jointure où les lignes sont retournées parce que l'information ne dit pas base de données comment limiter les résultats, de sorte que la requête retourne tous les lignes qui correspondent 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é le ID
de cars
et le model
de models
. Cependant, parce que nous n'avons pas spécifié comment pour rejoindre les tables, la base de données a apparié chaque "ligne 15191170920" de la première table avec chaque "ligne 15191170920" de la deuxième table.
Bon, le patron est de retour et il veut plus d'informations encore. je veux la même liste, mais aussi inclure 4WDs dans .
cela cependant, nous donne un grand excuse pour regarder deux façons différentes d'accomplir cela. Nous pourrions ajouter une autre condition à la clause où comme celle-ci:
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
alors que ce qui précède fonctionnera parfaitement, regardons-le différemment, c'est une excellente excuse pour montrer comment une requête union
fonctionnera.
nous savons que les suivants retourneront 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 4WDs:
select
a.ID,
b.model,
c.color
from
cars a
join models b
on a.model=b.ID
join colors c
on a.color=c.ID
join brands d
on a.brand=d.ID
where
b.ID=3
ainsi, en ajoutant une clause union all
entre eux, les résultats de la seconde requête seront annexé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 est renvoyée en premier, suivi par les résultats de la deuxième requête.
dans cet exemple, il aurait été bien sûr beaucoup plus facile d'utiliser simplement la première requête, mais les requêtes union
peuvent être excellentes pour des cas spécifiques. Ils sont un excellent moyen de retourner des résultats spécifiques de tableaux à partir de tableaux qui ne sont pas facilement réunis - ou d'ailleurs complètement tableaux non liés. 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 sera utilisé pour identifier l'ensemble des résultats.
- Le nombre de colonnes dans chaque requête doit être la même.
Maintenant, vous pourriez vous demandez ce que la différence est entre l'utilisation de union
et union all
. Une requête union
supprimera les doublons, tandis qu'une requête union all
ne le fera pas. Cela signifie qu'il y a un petit succès 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 ceci cependant.
sur cette note, il pourrait être intéressant 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, ajouter unorder by a.ID
entraînerait une erreur - en ce qui concerne les résultats, la colonne est appeléeID
plutôt quea.ID
- même si le même alias a été utilisé dans les deux requête. - nous ne pouvons avoir qu'une seule déclaration
order by
, et elle doit être la dernière.
pour les exemples suivants, j'ajoute quelques lignes supplémentaires à nos tableaux.
j'ai ajouté Holden
au tableau des marques.
J'ai aussi ajouté une ligne dans cars
qui a la valeur color
de 12
- qui n'a aucune référence dans la table de couleurs.
D'accord, le patron est encore une fois, aboyer demande dehors - * je veux un compte de chaque marque que nous portons et le nombre de voitures dans elle!"- Typiquement, nous arrivons juste à 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, quand nous joignons ceci à 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)
Qui est bien sûr un problème - nous ne voyons aucune mention de la belle Holden
marque j'ai ajouté.
c'est parce qu'une jointure cherche des lignes de correspondance dans les deux tableaux. Comme il n'y a pas de données dans les voitures de type Holden
il n'est pas renvoyé. C'est là que nous pouvons utiliser une jointure outer
. Ce sera retour tous les résultats d'un tableau, qu'ils soient appariés dans l'autre tableau ou non:
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 agrégée pour obtenir un compte et obtenir le patron de nos dos pour 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, plus de crânes de boss.
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 quel tableau est entièrement inclus. Un left outer join
inclura toutes les lignes du tableau sur la gauche, tandis que (vous l'avez deviné) un right outer join
apporte tous les résultats du tableau sur la droite dans les résultats.
certaines bases de données permettront un full outer join
qui rapportera des résultats (appariés ou non) de les deux tables, mais ce n'est pas pris en charge dans toutes les bases de données.
maintenant, je suppose qu'à ce moment - ci, vous vous demandez si oui ou non vous pouvez fusionner les types join dans une requête - et le la réponse est oui, vous 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 n'est-ce pas les résultats escomptés? C'est parce que bien que nous ayons sélectionné la jointure extérieure des voitures aux marques, il n'a pas été précisé dans la jointure aux couleurs - de sorte que la jointure particulière ne fera revenir les résultats qui concordent dans les deux tableaux.
Voici la requête qui fonctionnerait pour obtenir les résultats que nous nous attendions:
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 avoir deux jointures externes dans la requête et les résultats viennent à travers comme prévu.
maintenant, Que diriez-vous de ces autres types de jointures que vous demandez? Et les Intersections?
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 par une jointure (ou un énoncé bien structuré où au moins).
une Intersection est un type de jointure un peu similaire à un union
comme décrit ci - dessus-mais la différence est que seulement renvoie des lignes de données qui sont identiques (et je veux dire identiques) entre les différentes requêtes individuelles rejointes par l'union. 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
retournerait toutes les lignes de la table (la première requête en retournant tout ce qui dépasse ID>2
et le second Tout ce qui a ID<4
) ce qui donnerait un jeu complet, une requête intersect retournerait seulement la ligne correspondant à id=3
car il répond aux deux critères.
maintenant, si votre base de données ne supporte pas 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 entre deux tables différentes en utilisant une base de données cela ne supporte pas en soi une requête d'intersection, vous aurez besoin de 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 . D'autres qui peuvent lire ceci et peuvent penser que je me trompe sont libres à 101% de modifier et de critiquer ma réponse. ( honnêtement, je me sens très reconnaissant d'avoir corrigé mes erreurs. )
je vais poster quelques-unes des questions les plus fréquemment posées dans MySQL
balise.
astuce n ° 1 ( lignes qui correspondent à des conditions multiples )
vu 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
trouver tous les films qui appartiennent au moins à la fois Comedy
et Romance
catégories.
Solution
cette question peut parfois être très délicate. Il peut sembler qu'une requête de ce 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'
SQLFiddle Demo
ce qui est certainement très mal parce qu'il produit pas de résultat . L'explication de ceci est qu'il existe 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'opérateur AND
, les deux conditions doivent être vraies; sinon, elles seront fausses. Une autre requête est comme ceci,
SELECT DISTINCT a.MovieName
FROM MovieList a
INNER JOIN CategoryList b
ON a.ID = b.MovieID
WHERE b.CategoryName IN ('Comedy','Romance')
SQLFiddle Demo
et le résultat est encore incorrect parce qu'il correspond à l'enregistrement qui a au moins un match sur le categoryName
. La réelle solution serait en comptant le nombre d'instances d'enregistrement par film . Le nombre d'instance doit correspondre au nombre total de valeurs fournies en l'état.
SELECT a.MovieName
FROM MovieList a
INNER JOIN CategoryList b
ON a.ID = b.MovieID
WHERE b.CategoryName IN ('Comedy','Romance')
GROUP BY a.MovieName
HAVING COUNT(*) = 2
SQLFiddle Démo (la réponse)
truc 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 de chaque logiciel. Afficher les colonnes suivantes: SoftwareName
, Descriptions
, LatestVersion
( de la colonne VersionNo ), DateReleased
Solution
certains développeurs SQL utilisent par erreur MAX()
fonction agrégée. Ils ont tendance à créer comme ceci,
SELECT a.SoftwareName, a.Descriptions,
MAX(b.VersionNo) AS LatestVersion, b.DateReleased
FROM Software a
INNER JOIN VersionList b
ON a.ID = b.SoftwareID
GROUP BY a.ID
ORDER BY a.ID
SQLFiddle Demo
( la plupart des SGBDR génère une erreur de syntaxe sur ce parce que de ne pas spécifier certains de la non-agrégées colonnes sur la group by
clause ) le résultat produit correct LatestVersion
chaque logiciel mais évidemment le DateReleased
sont incorrects. MySQL
ne supporte pas Window Functions
et Common Table Expression
pourtant comme certains RDBMS le font déjà. La solution à ce problème est de créer un subquery
qui obtient le maximum individuel versionNo
sur chaque logiciel et plus tard être joint 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
SQLFiddle Démo (la réponse)
donc c'était il. Je vais poster un autre dès que je me souviens de n'importe quel autre FAQ sur MySQL
étiquette. Je vous remercie pour la lecture de ce petit article. J'espère que vous en avez au moins un peu appris.
UPDATE 1
astuce No 3 ( trouver le dernier record entre deux IDs )
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
Trouver 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
)
SQLFiddle Demo
Partie 2 - Les Sous-Requêtes
Ok, maintenant le patron a encore fait irruption - 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 le prochain tour dans notre sac de goodies SQL - le sous-jeu. Si vous n'êtes pas familier avec le terme, un sous-jeu est une requête qui court à l'intérieur d'une autre requête. Il existe de nombreuses façons de les utiliser.
pour notre demande, mettons d'abord une requête simple ensemble qui énumérera 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 wagons triés par marque, on peut 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 |
+--------+-----------+
donc, nous devrions être en mesure d'ajouter simplement la fonction count à notre requête d'origine 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, on ne peut 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 by - donc maintenant, quand la fonction count fonctionne, il n'y a qu'un seul ID par ID.
C'est là que nous pouvons cependant utiliser un sous - jeu-en fait nous pouvons faire deux types complètement différents de sous-jeu qui retourneront les mêmes résultats que 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 rangée de données, le sous-jeu s'exécute, obtenir une colonne de données et puis pop 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! ce serait faire de nous. Si vous avez remarqué, cette requête secondaire 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 différentes de voiture, mais la sous-série a couru onze fois que nous avons onze rangées de données que nous retournons. Donc, dans ce cas, ça ne semble pas être la façon la plus efficace d'écrire du code.
pour une approche différente, lançons un subquery et prétendre qu'il 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, donc nous avons 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 - ci) - mais les mêmes nombres droits.
alors, quelle est la différence entre les deux - et quand devrions-nous utiliser chaque type de sous-marin? Tout d'abord, assurons-nous de comprendre comment fonctionne la seconde requête. Nous avons sélectionné deux tableaux dans le from
clause de notre requête, puis a écrit une requête et a dit à la base de données qu'il était en fait une table à la place - qui la base de données est parfaitement heureux avec. Il peut être certains avantages à utiliser cette méthode (ainsi que certaines limites). Le plus important est que ce sous-query a couru 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 un tableau, nous devons apporter dans les rangées supplémentaires de données - de sorte qu'ils puissent être effectivement joints à nos rangées de données. Nous devons également être sûrs qu'il ya assez lignes de données si nous allons utiliser une jointure simple comme dans la requête ci-dessus. Si vous vous souvenez, la jointure ne tirera en arrière les lignes qui ont des données correspondantes sur les deux côtés de la jointure. Si nous ne faisons pas attention, cela pourrait entraîner des données valides n'étant pas retournés de notre table de voitures s'il n'y avait pas une rangée correspondante dans ce sous-requête.
maintenant, en regardant en arrière au premier sous-jeu, il y a aussi quelques limites. parce que nous ramenons des données dans une seule rangée, nous pouvons seulement tirer en arrière une rangée de données. Les sous-séries utilisées dans la clause select
d'une requête utilisent très souvent seulement une fonction agrégée telle que sum
, count
, max
ou une autre fonction agrégée similaire. Ils ne ont à, mais c'est souvent comment ils sont écrits.
donc, avant de passer à autre chose, jetons un coup d'oeil rapide à l'endroit où nous pouvons utiliser un sous-jeu. On peut 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 que c'est seulement un exemple, permet d'avoir un regard:
select
ID,
brand
from
brands
where
brand like '%o%'
+----+--------+
| ID | brand |
+----+--------+
| 1 | Ford |
| 2 | Toyota |
| 6 | Holden |
+----+--------+
3 rows in set (0.00 sec)
ceci nous renvoie une liste d'identificateurs de marque et de noms de marque (la deuxième colonne n'est ajoutée que pour nous montrer les marques) qui contiennent la lettre o
dans le nom.
maintenant, nous pourrions utiliser les résultats de cette requête dans une clause où 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 voir, même si le sous-magasin retournait les trois identifiants de la marque, notre table de voitures n'avait des entrées que pour deux d'entre eux.
dans ce cas, pour plus de détails, le sous-jeu fonctionne comme si nous écrivions 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 un sous-jeu vs entrées manuelles a changé l'ordre des lignes en revenant de la base de données.
pendant que nous discutons des sous-commandes, voyons ce que nous pouvons faire d'autre avec une sous-commande:
- vous pouvez placer un sous-jeu dans un autre sous-jeu, et ainsi de suite. Il y a une limite qui dépend de votre base de données, mais à moins de fonctions récursives d'un programmeur fou et maniaque, la plupart des gens ne toucheront jamais cette limite.
- vous pouvez placer un certain nombre de sous - séries dans une seule requête, quelques-unes dans la clause
select
, quelques-unes dans la clausefrom
et un couple plus dans la clausewhere
- juste se rappeler que chacun que vous mettez en rend votre requête plus complexe et susceptible de prendre plus de temps à exécuter.
si vous avez besoin d'écrire un code efficace, il peut être bénéfique d'écrire la requête un certain nombre de façons Et Voir (soit en la chronométrant ou en utilisant un expliquer plan) qui est la requête optimale pour obtenir vos résultats. La première façon qui fonctionne peut ne pas toujours être la meilleure façon de le faire.
Partie 3 - Trucs et Efficace du Code
MySQL() de l'efficacité
j'ai pensé que je voudrais ajouter certains des bits supplémentaires, des conseils et astuces.
une question que je vois venir un peu, est Comment puis-je obtenir des lignes non-appariées à partir de deux tableaux et je vois la réponse la plus communément acceptée comme quelque chose comme ce qui suit (basé sur nos voitures et marques de table-qui a Holden inscrit comme marque, mais ne figure pas dans le tableau cars):
select
a.ID,
a.brand
from
brands a
where
a.ID not in(select brand from cars)
Et oui il va travailler.
+----+--------+
| ID | brand |
+----+--------+
| 6 | Holden |
+----+--------+
1 row in set (0.00 sec)
mais il est pas efficace dans une base de données. Voici un lien à une question de débordement de la pile poser à ce sujet, et voici un excellent en profondeur article si vous voulez entrer dans la gravitation nitty.
la réponse courte est, si l'optimiseur ne le gère pas efficacement, il peut être beaucoup mieux d'utiliser une requête comme la suivante 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 le Tableau avec la même table dans la sous-requête
Ahhh, un autre ancien mais goodie - l'ancien Vous ne pouvez pas spécifier une table cible 'marques' pour la mise à jour DE la clause .
MySQL ne vous permettra pas d'exécuter un update...
requête avec un sous-Select sur la même table. Maintenant, vous pourriez penser, pourquoi ne pas simplement le mettre dans la clause où il faut? Mais que faire si vous voulez mettre à jour seulement la ligne avec la date max()
parmi un tas d'autres lignes? On ne peut pas faire ça dans une clause "où".
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 ya un contournement sournois qu'un nombre étonnamment grand d'utilisateurs ne savent pas-bien qu'il ne comprend certains hackery que vous aurez besoin de prêter attention à.
vous pouvez coller le sous-jeu dans un autre sous-jeu, ce qui met assez d'espace entre les deux requêtes pour qu'il fonctionne. Cependant, notez qu'il peut être plus sûr de coller la requête dans une transaction - cela empêchera tout autre changement étant fait aux tables pendant que la requête est en cours d'exécution.
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
vous pouvez utiliser le concept de requêtes multiples dans le mot-clé de 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 voulez. Utilisez les joints extérieurs et l'union où jamais il est nécessaire, même à l'intérieur des sous-séries de table.
C'est une méthode très facile d'impliquer autant de tables et de champs.
espère que cela lui permet de trouver les tables que vous lisez à travers la chose:
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 | |
+-------+-------------+------+-----+---------+----------------+