Найкращий спосіб перевірити, чи існує рядок у таблиці 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, оскільки ІСНУЄТЬСЯ лише тести на булеве значення на основі 1+ відповідності критеріям WHERE.
OMG Ponies

77
Хлопці, це прямо сказано в документації, пов’язаній з цією відповіддю, у другому абзаці: "Традиційно підзапит EXISTS починається з SELECT *, але він може починатися з SELECT 5 або SELECT column1 або будь-що взагалі. MySQL ігнорує список SELECT у таких підзапит, тому це не має ніякого значення ".
вр.

12
@ChrisThompson: що відбувається при виконанні оператора? Я маю на увазі, що містить набір результатів?
Ешвін

13
@Ashwin, він містить 0 (немає) або 1 (існує).
fedorqui 'ТАК перестаньте шкодити'

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.028197050095с.
  • 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,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.00023889541625977с.
  • SELECT EXISTS(SELECT 1 FROM test2 WHERE id ='321321' LIMIT 1): 0.00020313262939453с. (Швидше)

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

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

Я спробував на mysql, і у випадку, коли ви користуєтесь select 1 ... limit 1, марно оточувати вибраними існує
Adrien Horgnies

4
@LittleNooby є різниця. SELECT EXISTS ... дає справжнє та хибне значення (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 завжди створює додаткові навантаження для використання db, SELECT 1і він повертає 1, якщо ваш запис прямо там, інакше він повернеться в нулеве значення, і ви можете це обробити.


2

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


4
Однак це специфічно для БД. COUNT (*), як відомо, у PostgreSQL повільний. Краще було б вибрати стовпчик ПК і подивитися, чи він повертає якісь рядки.
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 може повернутися, коли вперше знайде рядок
буде

-1

Або ви можете вставити необмежену частину sql до умов, щоб у мене з'явився масив "terms" => ("Member.id НЕ ВИБІРЬСЯ)


-2

COUNT(*) оптимізовані в MySQL, тому колишній запит, швидше за все, буде швидшим, взагалі кажучи.


2
Ви маєте на увазі оптимізацію, яку має MyISAM для вибору підрахунку для цілої таблиці? Я не думав, що це допомогло, якщо був стан, де БУДЬ.
Бернард Чен
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.