Meilleure façon de tester si une ligne existe dans une table MySQL


337

J'essaie de savoir si une ligne existe dans une table. En utilisant MySQL, est-il préférable de faire une requête comme celle-ci:

SELECT COUNT(*) AS total FROM table1 WHERE ...

et vérifiez si le total est différent de zéro ou est-il préférable de faire une requête comme celle-ci:

SELECT * FROM table1 WHERE ... LIMIT 1

et vérifier si des lignes ont été retournées?

Dans les deux requêtes, la clause WHERE utilise un index.

Réponses:


470

Vous pouvez également essayer EXISTS:

SELECT EXISTS(SELECT * FROM table1 WHERE ...)

et selon la documentation , vous pouvez SELECTtout.

Traditionnellement, une sous-requête EXISTS commence par SELECT *, mais elle peut commencer par SELECT 5 ou SELECT column1 ou quoi que ce soit. MySQL ignore la liste SELECT dans une telle sous-requête, donc cela ne fait aucune différence.


30
Testez avec ...EXISTS( SELECT 1/0 FROM someothertable). Pour SQL Server et Oracle - cela ne fait aucune différence d'utiliser *, 1 ou NULL car EXISTS ne teste qu'un booléen basé sur 1+ des critères WHERE correspondant.
OMG Ponies

77
Les gars, il est dit dans la documentation liée à cette réponse, 2ème paragraphe, "Traditionnellement, une sous-requête EXISTS commence par SELECT *, mais elle peut commencer par SELECT 5 ou SELECT column1 ou quoi que ce soit du tout. MySQL ignore la liste SELECT dans une telle une sous-requête, donc cela ne fait aucune différence. "
mpen

12
@ChrisThompson: que se passe-t-il lorsque l'instruction est exécutée? Je veux dire que contient l'ensemble de résultats?
Ashwin

13
@Ashwin, il contient si un 0 (n'existe pas) ou 1 (existe).
fedorqui 'SO arrête de nuire'

10
Je pense que votre requête est superflue, j'ai testé, et cette requête SELECT 1 FROM table1 WHERE col = $var LIMIT 1est plus rapide que votre requête. Quel est donc l'avantage de votre requête?
Shafizadeh

182

J'ai fait quelques recherches sur ce sujet récemment. La façon de l'implémenter doit être différente si le champ est un champ TEXTE, un champ non unique.

J'ai fait quelques tests avec un champ TEXT. Compte tenu du fait que nous avons une table avec 1M d'entrées. 37 entrées sont égales à «quelque chose»:

  • SELECT * FROM test WHERE texte LIKE '%something%' LIMIT 1avec mysql_num_rows() : 0,039061069488525s. (PLUS RAPIDE)
  • SELECT count(*) as count FROM test WHERE text LIKE '%something% : 16.028197050095s.
  • SELECT EXISTS(SELECT 1 FROM test WHERE text LIKE '%something%') : 0.87045907974243s.
  • SELECT EXISTS(SELECT 1 FROM test WHERE text LIKE '%something%' LIMIT 1) : 0,044898986816406s.

Mais maintenant, avec un champ BIGINT PK, une seule entrée est égale à '321321':

  • SELECT * FROM test2 WHERE id ='321321' LIMIT 1avec mysql_num_rows() : 0,0089840888977051s.
  • SELECT count(*) as count FROM test2 WHERE id ='321321' : 0.00033879280090332s.
  • SELECT EXISTS(SELECT 1 FROM test2 WHERE id ='321321') : 0,00023889541625977s.
  • SELECT EXISTS(SELECT 1 FROM test2 WHERE id ='321321' LIMIT 1): 0.00020313262939453s. (PLUS RAPIDE)

2
Merci pour la réponse supplémentaire. Avez-vous trouvé que la différence de temps entre les deux options les plus rapides pour un champ TEXTE était assez cohérente? La différence ne semble pas grande et l'utilisation de SELECT EXISTS (SELECT 1 ... LIMIT 1) semble assez bonne dans les deux cas.
Bernard Chen

1
Vous avez raison, la différence n'est pas si importante en ce qui concerne les autres résultats concernant le champ de texte. Néanmoins, il serait peut-être préférable d'utiliser la requêteSELECT 1 FROM test WHERE texte LIKE '%something%' LIMIT 1
Laurent W.

J'ai essayé sur mysql et dans le cas où vous l'utilisez select 1 ... limit 1, il est inutile de s'entourer de select existe
Adrien Horgnies

4
@LittleNooby il y a une différence. SELECT EXISTS ... donne une valeur vraie et fausse (1 ou 0), tandis que SELECT 1 ... donne 1 ou vide. Il existe une différence subtile entre la valeur fausse et l'ensemble vide, selon votre situation.
Quickpick

@LittleNooby fait un excellent point, qui est facile à ignorer. Manquant dans les tests de chronométrage ci-dessus SELECT 1 FROM test WHERE ..., c'est sans SELECT EXISTSautour. Vraisemblablement, les cheveux sont plus rapides de cette façon.
ToolmakerSteve

27

Un bref exemple de la réponse de @ ChrisThompson

Exemple:

mysql> SELECT * FROM table_1;
+----+--------+
| id | col1   |
+----+--------+
|  1 | foo    |
|  2 | bar    |
|  3 | foobar |
+----+--------+
3 rows in set (0.00 sec)

mysql> SELECT EXISTS(SELECT 1 FROM table_1 WHERE id = 1);
+--------------------------------------------+
| EXISTS(SELECT 1 FROM table_1 WHERE id = 1) |
+--------------------------------------------+
|                                          1 |
+--------------------------------------------+
1 row in set (0.00 sec)

mysql> SELECT EXISTS(SELECT 1 FROM table_1 WHERE id = 9);
+--------------------------------------------+
| EXISTS(SELECT 1 FROM table_1 WHERE id = 9) |
+--------------------------------------------+
|                                          0 |
+--------------------------------------------+
1 row in set (0.00 sec)

Utilisation d'un alias:

mysql> SELECT EXISTS(SELECT 1 FROM table_1 WHERE id = 1) AS mycheck;
+---------+
| mycheck |
+---------+
|       1 |
+---------+
1 row in set (0.00 sec)

18

Dans mes recherches, je peux trouver le résultat à la vitesse suivante.

select * from table where condition=value
(1 total, Query took 0.0052 sec)

select exists(select * from table where condition=value)
(1 total, Query took 0.0008 sec)

select count(*) from table where condition=value limit 1) 
(1 total, Query took 0.0007 sec)

select exists(select * from table where condition=value limit 1)
(1 total, Query took 0.0006 sec) 

12

Je pense qu'il convient de souligner, bien que cela ait été évoqué dans les commentaires, que dans cette situation:

SELECT 1 FROM my_table WHERE *indexed_condition* LIMIT 1

Est supérieur à:

SELECT * FROM my_table WHERE *indexed_condition* LIMIT 1

En effet, la première requête peut être satisfaite par l'index, tandis que la seconde nécessite une recherche de ligne (sauf si toutes les colonnes de la table se trouvent dans l'index utilisé).

L'ajout de la LIMITclause permet au moteur de s'arrêter après avoir trouvé une ligne.

La première requête doit être comparable à:

SELECT EXISTS(SELECT * FROM my_table WHERE *indexed_condition*)

Ce qui envoie les mêmes signaux au moteur (1 / * ne fait aucune différence ici), mais j'écrirais toujours le 1 pour renforcer l'habitude lors de l'utilisation EXISTS:

SELECT EXISTS(SELECT 1 FROM my_table WHERE *indexed_condition*)

Il peut être judicieux d'ajouter l' EXISTShabillage si vous avez besoin d'un retour explicite lorsqu'aucune ligne ne correspond.


4

Nous vous suggérons de ne pas l'utiliser, Countcar le nombre crée toujours des charges supplémentaires pour l'utilisation de la base de données SELECT 1et renvoie 1 si votre enregistrement est là, sinon, il renvoie null et vous pouvez le gérer.


2

Une requête COUNT est plus rapide, bien que peut-être pas sensiblement, mais en ce qui concerne l'obtention du résultat souhaité, les deux devraient être suffisants.


4
Ceci est cependant spécifique à DB. Le COUNT (*) est connu pour être lent dans PostgreSQL. Il serait préférable de sélectionner la colonne PK et de voir si elle renvoie des lignes.
BalusC

3
COUNT (*) est cependant lent dans InnoDB
Will

2

Parfois, il est très pratique d'obtenir la clé primaire d'incrémentation automatique ( id) de la ligne si elle existe et 0si elle n'existe pas.

Voici comment cela peut être fait en une seule requête:

SELECT IFNULL(`id`, COUNT(*)) FROM WHERE ...

Pourquoi ne pas simplement utiliser IFNULL(id, 0)ici au lieu de COUNT(*)?
Ethan Hohensee


-1

J'irais avec COUNT(1). C'est plus rapide que COUNT(*)parce que des COUNT(*)tests pour voir si au moins une colonne de cette ligne est! = NULL. Vous n'en avez pas besoin, surtout parce que vous avez déjà une condition en place (la WHEREclause). COUNT(1)teste plutôt la validité de 1, qui est toujours valide et prend beaucoup moins de temps à tester.


8
-1 C'est faux. COUNT (*) ne regarde pas les valeurs des colonnes - il compte juste le nombre de lignes. Voir ma réponse ici: stackoverflow.com/questions/2876909/…
Mark Byers

6
COUNT () est beaucoup plus lent qu'EXISTS car EXISTS peut revenir lorsqu'il trouve une ligne pour la première fois
Will

-1

Ou vous pouvez insérer une partie SQL brute dans les conditions pour que j'aie 'conditions' => tableau ('Member.id NOT IN (SELECT Membership.member_id FROM memberships AS Membership)'))


-2

COUNT(*) sont optimisés dans MySQL, donc l'ancienne requête est susceptible d'être plus rapide, d'une manière générale.


2
Faites-vous référence à l'optimisation de MyISAM pour sélectionner le nombre pour une table entière? Je ne pense pas que cela ait aidé s'il y avait une condition OERE.
Bernard Chen
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.