Der beste Weg, um zu testen, ob eine Zeile in einer MySQL-Tabelle vorhanden ist

336

Ich versuche herauszufinden, ob eine Zeile in einer Tabelle vorhanden ist. Mit MySQL ist es besser, eine Abfrage wie folgt durchzuführen:

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

und prüfen Sie, ob die Summe nicht Null ist oder ob es besser ist, eine Abfrage wie folgt durchzuführen:

SELECT * FROM table1 WHERE ... LIMIT 1

und prüfen, ob Zeilen zurückgegeben wurden?

In beiden Abfragen verwendet die WHERE-Klausel einen Index.

Bernard Chen
quelle

Antworten:

469

Sie könnten auch versuchen EXISTS:

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

und laut Dokumentation können Sie SELECTalles.

Traditionell beginnt eine EXISTS-Unterabfrage mit SELECT *, sie kann jedoch auch mit SELECT 5 oder SELECT column1 oder irgendetwas anderem beginnen. MySQL ignoriert die SELECT-Liste in einer solchen Unterabfrage, daher macht es keinen Unterschied.

Chris Thompson
quelle
30
Testen Sie mit ...EXISTS( SELECT 1/0 FROM someothertable). Für SQL Server und Oracle macht es keinen Unterschied, *, 1 oder NULL zu verwenden, da EXISTS nur einen Booleschen Wert testet, der auf 1+ der übereinstimmenden WHERE-Kriterien basiert.
OMG Ponys
77
Leute, es steht direkt in der Dokumentation, die in dieser Antwort, 2. Absatz, verlinkt ist: "Traditionell beginnt eine EXISTS-Unterabfrage mit SELECT *, aber sie könnte mit SELECT 5 oder SELECT column1 oder irgendetwas beginnen. MySQL ignoriert die SELECT-Liste in solchen eine Unterabfrage, also macht es keinen Unterschied. "
Mpen
12
@ChrisThompson: Was passiert, wenn die Anweisung ausgeführt wird? Ich meine, was enthält die Ergebnismenge?
Ashwin
13
@Ashwin, es enthält, ob eine 0 (nicht vorhanden) oder 1 (vorhanden) vorhanden ist.
Fedorqui 'SO hör auf zu schaden'
10
Ich denke, Ihre Abfrage ist überflüssig, habe ich getestet, und diese Abfrage SELECT 1 FROM table1 WHERE col = $var LIMIT 1ist schneller als Ihre Abfrage. Was ist der Vorteil Ihrer Anfrage?
Shafizadeh
182

Ich habe kürzlich einige Untersuchungen zu diesem Thema durchgeführt. Die Art und Weise der Implementierung muss unterschiedlich sein, wenn das Feld ein TEXT-Feld ist, ein nicht eindeutiges Feld.

Ich habe einige Tests mit einem TEXT-Feld durchgeführt. In Anbetracht der Tatsache, dass wir eine Tabelle mit 1M Einträgen haben. 37 Einträge sind gleich 'etwas':

  • SELECT * FROM test WHERE texte LIKE '%something%' LIMIT 1mit mysql_num_rows() : 0.039061069488525s. (SCHNELLER)
  • 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.

Aber jetzt, mit einem BIGINT PK-Feld, ist nur ein Eintrag gleich '321321':

  • SELECT * FROM test2 WHERE id ='321321' LIMIT 1mit 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. (SCHNELLER)
Laurent W.
quelle
2
Danke für die zusätzliche Antwort. Haben Sie festgestellt, dass der Zeitunterschied zwischen den beiden schnellsten Optionen für ein TEXT-Feld ziemlich konsistent ist? Der Unterschied scheint nicht groß zu sein, und die Verwendung von SELECT EXISTS (SELECT 1 ... LIMIT 1) scheint in beiden Fällen ziemlich gut zu sein.
Bernard Chen
1
Sie haben Recht, der Unterschied ist in Bezug auf die anderen Ergebnisse in Bezug auf das Textfeld nicht so wichtig. Trotzdem wäre die Abfrage vielleicht besser mitSELECT 1 FROM test WHERE texte LIKE '%something%' LIMIT 1
Laurent W.
Ich habe MySQL ausprobiert und für den Fall, dass Sie es verwenden select 1 ... limit 1, ist es nutzlos, mit Select zu umgeben
Adrien Horgnies
4
@ LittleNooby gibt es einen Unterschied. SELECT EXISTS ... gibt den wahren und falschen Wert (1 oder 0) an, während SELECT 1 ... entweder 1 oder leer ergibt. Abhängig von Ihrer Situation gibt es einen subtilen Unterschied zwischen falschem Wert und leerem Satz.
Quickpick
@LittleNooby macht einen ausgezeichneten Punkt, der leicht zu übersehen ist. In den obigen Timing-Tests fehlt SELECT 1 FROM test WHERE ..., ohne es zu SELECT EXISTSumgehen. Vermutlich ist ein Haar so schneller.
ToolmakerSteve
27

Ein kurzes Beispiel für die Antwort von @ ChrisThompson

Beispiel:

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)

Verwenden eines Alias:

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

In meiner Forschung kann ich feststellen, dass das Ergebnis der folgenden Geschwindigkeit folgt.

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) 
Shihab mm
quelle
12

Ich halte es für erwähnenswert, obwohl in den Kommentaren darauf hingewiesen wurde, dass in dieser Situation:

SELECT 1 FROM my_table WHERE *indexed_condition* LIMIT 1

Ist überlegen:

SELECT * FROM my_table WHERE *indexed_condition* LIMIT 1

Dies liegt daran, dass die erste Abfrage vom Index erfüllt werden kann, während die zweite eine Zeilensuche erfordert (es sei denn, möglicherweise befinden sich alle Spalten der Tabelle im verwendeten Index).

Durch Hinzufügen der LIMITKlausel kann die Engine gestoppt werden, nachdem eine Zeile gefunden wurde.

Die erste Abfrage sollte vergleichbar sein mit:

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

Was die gleichen Signale an den Motor sendet (1 / * macht hier keinen Unterschied), aber ich würde trotzdem die 1 schreiben, um die Gewohnheit bei der Verwendung zu verstärken EXISTS:

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

Es kann sinnvoll sein, die EXISTSUmhüllung hinzuzufügen , wenn Sie eine explizite Rückgabe benötigen, wenn keine Zeilen übereinstimmen.

Arth
quelle
4

Schlagen Sie vor, dass Sie nicht verwenden, Countda count immer zusätzliche Ladevorgänge für die Datenbankverwendung ausführt SELECT 1und 1 zurückgibt, wenn Ihr Datensatz genau dort ist, andernfalls null zurückgibt und Sie damit umgehen können.

Fatih Karatana
quelle
2

Eine COUNT- Abfrage ist schneller, wenn auch möglicherweise nicht merklich, aber um das gewünschte Ergebnis zu erzielen, sollten beide ausreichend sein.

Jaywon
quelle
4
Dies ist jedoch DB-spezifisch. Es ist bekannt, dass COUNT (*) in PostgreSQL langsam ist. Besser wäre es, die PK-Spalte auszuwählen und zu prüfen, ob sie Zeilen zurückgibt.
BalusC
3
COUNT (*) ist in InnoDB allerdings langsam
Will
2

Manchmal ist es sehr praktisch, den Auto-Inkrement-Primärschlüssel ( id) der Zeile abzurufen, wenn er vorhanden ist und 0nicht.

So geht das in einer einzigen Abfrage:

SELECT IFNULL(`id`, COUNT(*)) FROM WHERE ...
Zaxter
quelle
Warum nicht einfach IFNULL(id, 0)hier anstelle des verwenden COUNT(*)?
Ethan Hohensee
-1

Ich würde mitgehen COUNT(1). Es ist schneller alsCOUNT(*) weil COUNT(*)Tests, um festzustellen, ob mindestens eine Spalte in dieser Zeile! = NULL ist. Das brauchen Sie nicht, vor allem, weil Sie bereits eine Bedingung (die WHEREKlausel) haben. COUNT(1)testet stattdessen die Gültigkeit von 1, was immer gültig ist und viel weniger Zeit zum Testen benötigt.

Felix
quelle
8
-1 Das ist falsch. COUNT (*) betrachtet die Spaltenwerte nicht - es zählt nur die Anzahl der Zeilen. Siehe meine Antwort hier: stackoverflow.com/questions/2876909/…
Mark Byers
6
COUNT () ist viel langsamer als EXISTS, da EXISTS zurückkehren kann, wenn es zum ersten Mal eine Zeile findet
Will
-1

Oder Sie können einen unformatierten SQL-Teil in Bedingungen einfügen, sodass ich 'Bedingungen' => Array habe ('Member.id NOT IN (SELECT Membership.member_id FROM Mitgliedschaften AS Mitgliedschaft)')

user4193303
quelle
-2

COUNT(*) sind in MySQL optimiert, sodass die vorherige Abfrage im Allgemeinen wahrscheinlich schneller ist.

Arthur Reutenauer
quelle
2
Beziehen Sie sich auf die Optimierung, die MyISAM für die Auswahl der Anzahl für eine ganze Tabelle hat? Ich dachte nicht, dass das helfen würde, wenn es eine WHERE-Bedingung gäbe.
Bernard Chen