Najlepszy sposób na sprawdzenie, czy wiersz istnieje w tabeli MySQL


337

Próbuję dowiedzieć się, czy wiersz istnieje w tabeli. Korzystając z MySQL, lepiej wykonać takie zapytanie:

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

i sprawdź, czy suma jest różna od zera, czy lepiej jest wykonać takie zapytanie:

SELECT * FROM table1 WHERE ... LIMIT 1

i sprawdź, czy zostały zwrócone jakieś wiersze?

W obu zapytaniach klauzula WHERE używa indeksu.

Odpowiedzi:


470

Możesz także spróbować EXISTS:

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

i zgodnie z dokumentacją możesz SELECTwszystko.

Tradycyjnie podzapytanie EXISTS zaczyna się od SELECT *, ale może zaczynać się od SELECT 5 lub SELECT column1 lub cokolwiek innego. MySQL ignoruje listę SELECT w takim podzapytaniu, więc nie ma znaczenia.


30
Przetestuj za pomocą ...EXISTS( SELECT 1/0 FROM someothertable). W przypadku SQL Server i Oracle - użycie *, 1 lub NULL nie ma znaczenia, ponieważ ISTNIEJE testuje tylko wartość logiczną na podstawie 1+ zgodnych kryteriów WHERE.
OMG Ponies

77
Chłopaki, mówi wprost w dokumentacji powiązanej z odpowiedzią w drugim akapicie: „Tradycyjnie podzapytanie EXISTS zaczyna się od SELECT *, ale może zaczynać się od SELECT 5 lub SELECT kolumna 1 lub cokolwiek w ogóle. MySQL ignoruje listę SELECT w takich podzapytanie, więc nie ma znaczenia. ”
mpen

12
@ChrisThompson: co się stanie, gdy instrukcja zostanie wykonana? Mam na myśli, co zawiera zestaw wyników?
Ashwin,

13
@ Ashwin, zawiera informację, czy 0 (nie istnieje) czy 1 (istnieje).
fedorqui „SO przestań szkodzić”

10
Myślę, że twoje zapytanie jest zbyteczne, przetestowałem je, a to zapytanie SELECT 1 FROM table1 WHERE col = $var LIMIT 1jest szybsze niż twoje zapytanie. Jaka jest zatem zaleta twojego zapytania?
Shafizadeh

182

Ostatnio przeprowadziłem kilka badań na ten temat. Sposób implementacji musi być inny, jeśli pole jest polem TEXT, polem nie unikalnym.

Zrobiłem kilka testów z polem TEXT. Biorąc pod uwagę fakt, że mamy tabelę z wpisami 1M. 37 wpisów jest równych „coś”:

  • SELECT * FROM test WHERE texte LIKE '%something%' LIMIT 1z mysql_num_rows() : 0,039061069488525s. (SZYBCIEJ)
  • 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.

Ale teraz, z polem BIGINT PK, tylko jeden wpis jest równy „321321”:

  • SELECT * FROM test2 WHERE id ='321321' LIMIT 1z 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. (SZYBCIEJ)

2
Dziękuję za dodatkową odpowiedź. Czy zauważyłeś, że różnica w czasie między dwiema najszybszymi opcjami pola TEKST jest dość spójna? Różnica nie wydaje się duża, a użycie SELECT EXISTS (SELECT 1 ... LIMIT 1) wydaje się całkiem dobre w obu przypadkach.
Bernard Chen

1
Masz rację, różnica nie jest tak ważna w odniesieniu do innych wyników dotyczących pola tekstowego. Niemniej jednak może lepiej byłoby użyć zapytaniaSELECT 1 FROM test WHERE texte LIKE '%something%' LIMIT 1
Laurent W.

Próbowałem na mysql, a jeśli używasz select 1 ... limit 1, nie ma sensu otaczać go za pomocą select
Adrien Horgnies

4
@LittleNooby jest różnica. WYBIERZ ISTNIEJE ... daje wartość prawda i fałsz (1 lub 0), podczas gdy WYBIERZ 1 ... daje albo 1, albo pusty. Istnieją subtelne różnice między wartością fałszywą a pustym zestawem, w zależności od sytuacji.
Quickpick

@LittleNooby stanowi doskonały punkt, który łatwo przeoczyć. Brakuje w powyższych testach czasowych SELECT 1 FROM test WHERE ..., bez SELECT EXISTStego. Prawdopodobnie w ten sposób włosy są szybsze.
ToolmakerSteve,

27

Krótki przykład odpowiedzi @ ChrisThompson

Przykład:

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)

Korzystanie z aliasu:

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

18

W moich badaniach mogę znaleźć wynik postępujący z prędkością.

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

Wydaje mi się, że warto zwrócić uwagę, choć poruszono to w komentarzach, że w tej sytuacji:

SELECT 1 FROM my_table WHERE *indexed_condition* LIMIT 1

Jest lepszy od:

SELECT * FROM my_table WHERE *indexed_condition* LIMIT 1

Wynika to z faktu, że pierwsze zapytanie może być spełnione przez indeks, podczas gdy drugie wymaga wyszukiwania wiersza w górę (chyba że wszystkie kolumny tabeli znajdują się w użytym indeksie).

Dodanie LIMITklauzuli pozwala zatrzymać silnik po znalezieniu dowolnego wiersza.

Pierwsze zapytanie powinno być porównywalne z:

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

Który wysyła te same sygnały do ​​silnika (1 / * nie ma tutaj znaczenia), ale nadal zapisałbym 1, aby wzmocnić nawyk podczas używania EXISTS:

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

Sensowne może być dodanie EXISTSopakowania, jeśli wymagany jest wyraźny zwrot, gdy żadne wiersze nie pasują.


4

Sugeruj, abyś nie używał, Countponieważ count zawsze powoduje dodatkowe obciążenia dla użycia bazy danych SELECT 1i zwraca 1, jeśli twój rekord w tym miejscu zwraca wartość null i możesz to obsłużyć.


2

COUNT zapytania jest szybsza, choć może nie zauważalnie, ale jeśli chodzi o uzyskanie pożądanego rezultatu, oba powinny być wystarczające.


4
Jest to jednak specyficzne dla DB. COUNT (*) jest znany jako wolny w PostgreSQL. Lepiej byłoby wybrać kolumnę PK i sprawdzić, czy zwraca jakieś wiersze.
BalusC,

3
COUNT (*) jest jednak wolny w InnoDB
będzie

2

Czasami bardzo przydatne jest uzyskanie klucza podstawowego automatycznego przyrostu ( id) wiersza, jeśli istnieje i0 jeśli nie.

Oto, jak można to zrobić za pomocą jednego zapytania:

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

Dlaczego nie po prostu użyć IFNULL(id, 0)tutaj zamiast COUNT(*)?
Ethan Hohensee


-1

Poszedłbym z COUNT(1). Jest szybszy niż COUNT(*)ponieważ COUNT(*)testy sprawdzają, czy przynajmniej jedna kolumna w tym wierszu ma wartość! = NULL. Nie potrzebujesz tego, zwłaszcza że masz już warunek ( WHEREklauzula). COUNT(1)zamiast tego testuje ważność 1, która jest zawsze ważna i zajmuje dużo mniej czasu na testowanie.


8
-1 To jest złe. COUNT (*) nie patrzy na wartości kolumn - po prostu liczy liczbę wierszy. Zobacz moją odpowiedź tutaj: stackoverflow.com/questions/2876909/…
Mark Byers

6
COUNT () jest znacznie wolniejszy niż ISTNIEJE, ponieważ ISTNIEJE może powrócić, gdy pierwszy raz znajdzie wiersz
Czy

-1

Lub możesz wstawić surową część sql do warunków, więc mam „warunki” => tablica („Member.id NOT IN (SELECT Membership.member_id Z członkostwa AS Członkostwo)”)


-2

COUNT(*) są zoptymalizowane w MySQL, więc poprzednie zapytanie prawdopodobnie będzie szybsze.


2
Czy masz na myśli optymalizację, jaką MyISAM ma do wyboru liczby dla całej tabeli? Nie sądziłem, że to pomogło, gdyby był GDZIE stan.
Bernard Chen,
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.