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.