Лучший способ проверить, существует ли строка в таблице MySQL


337

Я пытаюсь выяснить, существует ли строка в таблице. Используя MySQL, лучше сделать запрос следующим образом:

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

и проверьте, если сумма не равна нулю или лучше сделать запрос, как это:

SELECT * FROM table1 WHERE ... LIMIT 1

и проверить, были ли возвращены какие-либо строки?

В обоих запросах предложение WHERE использует индекс.

Ответы:


470

Вы также можете попробовать EXISTS:

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

и согласно документации , вы можете SELECTчто угодно.

Традиционно подзапрос EXISTS начинается с SELECT *, но он может начинаться с SELECT 5 или SELECT column1 или чего-либо еще. MySQL игнорирует список SELECT в таком подзапросе, поэтому это не имеет значения.


30
Тест с ...EXISTS( SELECT 1/0 FROM someothertable). Для SQL Server и Oracle - нет разницы, использовать *, 1 или NULL, потому что EXISTS проверяет только логическое значение на основе 1+ соответствия критерия WHERE.
OMG Ponies

77
Ребята, прямо в документации, связанной с этим ответом, во втором абзаце, говорится: «Традиционно подзапрос EXISTS начинается с SELECT *, но он может начинаться с SELECT 5 или SELECT column1 или чего угодно. MySQL игнорирует список SELECT в таком случае. подзапрос, так что это не имеет значения ".
mpen

12
@ChrisThompson: что происходит при выполнении оператора? Я имею в виду, что содержится в наборе результатов?
Эшвин

13
@Ashwin, он содержит 0 (не существует) или 1 (существует).
Федорки 'ТАК прекрати вредить'

10
Я думаю, ваш запрос излишен, я проверял, и этот запрос SELECT 1 FROM table1 WHERE col = $var LIMIT 1быстрее, чем ваш запрос. Так в чем же преимущество вашего запроса?
Шафизаде

182

Я недавно провел несколько исследований на эту тему. Способ его реализации должен быть другим, если это поле TEXT, неуникальное поле.

Я сделал несколько тестов с полем TEXT. Учитывая тот факт, что у нас есть таблица с 1М записей. 37 записей равны «чему-то»:

  • SELECT * FROM test WHERE texte LIKE '%something%' LIMIT 1с mysql_num_rows() : 0,039061069488525 с. (БЫСТРЕЕ)
  • SELECT count(*) as count FROM test WHERE text LIKE '%something% : 16.028197050095s.
  • SELECT EXISTS(SELECT 1 FROM test WHERE text LIKE '%something%') 0,87045907974243.
  • SELECT EXISTS(SELECT 1 FROM test WHERE text LIKE '%something%' LIMIT 1) : 0.044898986816406с.

Но теперь, с полем BIGINT PK, только одна запись равна '321321':

  • SELECT * FROM test2 WHERE id ='321321' LIMIT 1с mysql_num_rows() : 0,0089840888977051с.
  • 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. (БЫСТРЕЕ)

2
Спасибо за дополнительный ответ. Считаете ли вы разницу во времени между двумя самыми быстрыми вариантами для поля TEXT довольно последовательной? Разница не кажется большой, и использование SELECT EXISTS (SELECT 1 ... LIMIT 1) кажется довольно хорошим в обоих случаях.
Бернард Чен

1
Вы правы, разница не так важна в отношении других результатов, касающихся текстового поля. Тем не менее, возможно, запрос будет лучше использоватьSELECT 1 FROM test WHERE texte LIKE '%something%' LIMIT 1
Лоран В.

Я пробовал на MySQL, и в случае, если вы используете select 1 ... limit 1, бесполезно окружать избранным существует
Adrien Horgnies

4
@LittleNooby есть разница. SELECT EXISTS ... дает значение true и false (1 или 0), а SELECT 1 ... дает либо 1, либо пусто. Существует тонкая разница между ложным значением и пустым набором, в зависимости от вашей ситуации.
Quickpick

@LittleNooby делает отличную мысль, которую легко не заметить. Отсутствует во временных тестах выше SELECT 1 FROM test WHERE ..., без SELECT EXISTSнего. Предположительно, так волосы быстрее.
ToolmakerSteve

27

Краткий пример ответа @ ChrisThompson

Пример:

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)

Используя псевдоним:

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

18

В своих исследованиях я могу найти результат, набирающий скорость.

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

Я чувствую, что стоит отметить, хотя в комментариях было затронуто, что в этой ситуации:

SELECT 1 FROM my_table WHERE *indexed_condition* LIMIT 1

Превосходит:

SELECT * FROM my_table WHERE *indexed_condition* LIMIT 1

Это связано с тем, что первый запрос может быть удовлетворен индексом, тогда как второй требует поиска строки (если, возможно, все столбцы таблицы не включены в используемый индекс).

Добавление LIMITпункта позволяет двигателю остановиться после нахождения любого ряда.

Первый запрос должен быть сопоставим с:

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

Который посылает те же сигналы в движок (1 / * здесь не имеет значения), но я все равно написал бы 1, чтобы усилить привычку при использовании EXISTS:

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

Возможно, имеет смысл добавить EXISTSперенос, если вам требуется явный возврат, если ни одна строка не совпадает.


4

Рекомендуем вам не использовать, Countпотому что count всегда создает дополнительные нагрузки для использования базы данных SELECT 1и возвращает 1, если ваша запись тут же, в противном случае возвращается null, и вы можете справиться с этим.


2

COUNT запрос быстрее, хотя , возможно , не заметно, но по мере получения желаемого результата, как должно быть достаточно.


4
Это, однако, специфично для БД. COUNT (*), как известно, работает медленно в PostgreSQL. Лучше было бы выбрать столбец PK и посмотреть, возвращает ли он какие-либо строки.
BalusC

3
COUNT (*) медленно работает в InnoDB
Will

2

Иногда очень удобно получить первичный ключ с автоинкрементом ( id) строки, если он существует, а 0если нет.

Вот как это можно сделать в одном запросе:

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

Почему бы просто не использовать IFNULL(id, 0)здесь вместо COUNT(*)?
Итан


-1

Я бы пошел с COUNT(1). Это быстрее , чем COUNT(*)потому , что COUNT(*)тесты , чтобы увидеть , если хотя бы один столбец в этой строке! = NULL. Вам это не нужно, особенно потому, что у вас уже есть условие ( WHEREпункт). COUNT(1)вместо этого проверяет достоверность 1, которая всегда действительна и требует гораздо меньше времени для проверки.


8
-1 Это неправильно. COUNT (*) не смотрит на значения столбца - он просто считает количество строк. Смотрите мой ответ здесь: stackoverflow.com/questions/2876909/…
Марк Байерс

6
COUNT () намного медленнее, чем EXISTS, поскольку EXISTS может вернуться, когда он впервые найдет строку
Will

-1

Или вы можете вставить необработанную часть SQL в условия, чтобы у меня было «условие» => массив («Member.id NOT IN (SELECT Membership.member_id FROM memberships AS Membership)»)


-2

COUNT(*) оптимизированы в MySQL, поэтому первый запрос, скорее всего, будет быстрее, вообще говоря.


2
Вы имеете в виду оптимизацию, которую имеет MyISAM для выбора счетчика для всей таблицы? Я не думаю, что это помогло, если бы было состояние ГДЕ.
Бернард Чен
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.