MySQL: найшвидший спосіб підрахунку кількості рядків


117

Який спосіб підрахунку кількості рядків повинен бути швидшим у MySQL?

Це:

SELECT COUNT(*) FROM ... WHERE ...

Або альтернатива:

SELECT 1 FROM ... WHERE ...

// and then count the results with a built-in function, e.g. in PHP mysql_num_rows()

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


1
О, я знайшов подібне запитання ( stackoverflow.com/questions/1855226/… ). Але тоді я використовую SELECT 1і ні SELECT *. Чи є різниця?
Франц

я не знаю, але можливо, що ці два відповіді однакові - оптимізатор запитів mysql може робити те саме на кожному. що сказане перше менш неоднозначне, ніж друге. чому б вам не написати деякі орієнтири і не перевірити їх?
Джессі Коен

Ум, припустимо, я намагаюся покращити видимість пошукової системи SO, задаючи подібне запитання різними словами;)
Франц

1
Різниця - кількість даних, що надсилаються на сторону PHP. Чим більше у вас стовпців, тим повільніше SELECT * отримує відносно SELECT 1, оскільки всі стовпці витягуються замість просто числа 1. mysql_query()Наприклад, коли ви запускаєте , весь набір результатів надсилається на PHP з MySQL, незалежно від того, що ви робити з цими даними.
toon81

Поставити таке запитання - це чудовий спосіб отримати уявлення або нові ідеї, але в кінцевому підсумку, якщо у вас є конкретний сценарій, де ви хочете більшої швидкості, вам доведеться запустити тести, щоб побачити, що найшвидше.
still_dreaming_1

Відповіді:


124

Коли ви COUNT(*)приймете підрахунок індексів стовпців, то це буде найкращим результатом. Mysql з двигуном MyISAM насправді зберігає кількість рядків, він не враховує всі рядки кожного разу, коли ви намагаєтеся підрахувати всі рядки. (на основі стовпця первинного ключа)

Використовувати PHP для підрахунку рядків не дуже розумно, тому що вам доведеться надсилати дані з mysql до php. Чому це робити, коли ти можеш досягти того ж з боку mysql?

Якщо COUNT(*)повільний показник повільно, слід запустити EXPLAINзапит і перевірити, чи справді використовуються індекси та де їх слід додати.


Далі не найшвидший спосіб, але є випадок, коли COUNT(*)насправді не підходить - коли ви починаєте групувати результати, ви можете зіткнутися з проблемою, де COUNTнасправді не нараховуються всі рядки.

Рішення є SQL_CALC_FOUND_ROWS. Зазвичай це використовується, коли ви вибираєте рядки, але все-таки потрібно знати загальну кількість рядків (наприклад, для підкачки). Вибираючи рядки даних, просто додайте SQL_CALC_FOUND_ROWSключове слово після SELECT:

SELECT SQL_CALC_FOUND_ROWS [needed fields or *] FROM table LIMIT 20 OFFSET 0;

Вибравши потрібні рядки, ви можете отримати підрахунок за допомогою цього єдиного запиту:

SELECT FOUND_ROWS();

FOUND_ROWS() потрібно викликати відразу після запиту вибору даних.


На закінчення, все насправді зводиться до того, скільки записів у вас є і що у викладі WHERE. Ви дійсно повинні звернути увагу на те, як використовуються індекси, коли існує багато рядків (десятки тисяч, мільйони і вище).


14
Виправлення: MyISAMзберігає кількість рядків. Інші двигуни для зберігання даних, як-от InnoDB не зберігають кількість рядків, і підраховуватимуть усі рядки кожного разу .
The Scrum Meister

1
Чи знаєте ви, що буде найшвидшим, коли ви просто захочете з’ясувати, чи є ряд: SELECT 1 FROM ... LIMIT 1чи SELECT COUNT(*) FROM ...?
Франц

1
Це, мабуть, корисно зауважити, що якщо вам потрібні дані так чи інакше ви хочете лише підрахунок для пагинації / тощо. ефективніше отримувати дані, а потім рахувати рядки у вашій програмі.
Тизоїд

6
Не має значення, чи враховується кількість рядків двигунів. У питанні чітко зазначено, що є WHEREпункт.
Альваро Гонсалес

1
@Franz SELECT COUNT(*) FROM ...може зайняти багато часу, залежно від того, що потрібно сканувати (наприклад, дуже велика таблиця або індекс мільйонів / мільярдів / трильйони рядків). SELECT 1 FROM ... LIMIT 1повертається негайно, оскільки ви обмежуєте його до першого ряду.
jbo5112

59

Після розмови зі своїми товаришами по команді Рікардо сказав нам, що швидший шлях:

show table status like '<TABLE NAME>' \G

Але ви повинні пам’ятати, що результат може бути не точним.

Ви можете використовувати його і з командного рядка:

$ mysqlshow --status <DATABASE> <TABLE NAME>

Більше інформації: http://dev.mysql.com/doc/refman/5.7/uk/show-table-status.html

І ви можете знайти повне обговорення на сайті mysqlperformanceblog


2
Для InnoDB це наближення.
Мартін Турноїй

2
Це чудово знати, коли потрібно грубе уявлення про кількість рядків у дуже великих таблицях, де count (*) може буквально зайняти години!
Марк Хансен

Це врятувало мене від витягування всіх моїх волосся. COUNT (*) займає віки, щоб нарахувати всі 33 мільйони плюс рядки в моїй базі даних. У будь-якому разі, я хотів лише знати, чи працює моя паралелізована функція видалення рядків чи ні. Точне число мені не знадобилося.
joemar.ct

1
+1 Використання статусу таблиці замість "COUNT (*)" має бути правильною відповіддю на це питання, оскільки це стосується "найшвидшого", а не "точності".
лепе

2
Використання SHOW TABLE STATUS(або еквівалент SELECTв information_schema) швидко, але воно не обробляє WHEREзастереження. Це точно для MyISAM, але неточно (іноді відключається в 2 рази) для InnoDB.
Рік Джеймс

29

Чудове запитання, чудові відповіді. Ось швидкий спосіб повторити результати, якщо хтось читає цю сторінку та пропускає цю частину:

$counter = mysql_query("SELECT COUNT(*) AS id FROM table");
$num = mysql_fetch_array($counter);
$count = $num["id"];
echo("$count");

5
mysql_query - це застаріла функція з PHP 5.5.0.
Омар Тарік

8
Чому ні as count? idплутає з першого погляду.
Орхан Аліханов

Не відповідає на запитання
mentalic

17

Цей запит (який схожий на опублікований bayuah ) показує хороший підсумок підрахунку всіх таблиць у базі даних: (спрощена версія збереженої процедури Івана Качікатарі, яку я дуже рекомендую).

SELECT TABLE_NAME AS 'Table Name', TABLE_ROWS AS 'Rows' FROM information_schema.TABLES WHERE TABLES.TABLE_SCHEMA = '`YOURDBNAME`' AND TABLES.TABLE_TYPE = 'BASE TABLE'; 

Приклад:

+-----------------+---------+
| Table Name      | Rows    |
+-----------------+---------+
| some_table      |   10278 |
| other_table     |     995 |

Це дає мені результат. Але результати підрахунку (1) і цього є різними. Цей спосіб дає меншу кількість завжди, ніж кількість запитів. Будь-які думки?
Айяппан Секар

3
Просто записка до читачів. Цей метод надзвичайно швидкий, але він застосований лише тоді, коли ви можете працювати з приблизною кількістю рядків, оскільки значення, збережене в information_schema, не таке, як повернене SELECT count(*) FROMу випадку використання InnoDB. Якщо вам потрібно суворе значення, майте на увазі, що цей метод дає суворе значення лише для таблиць MyISAM. З InnoDB кількість рядків є приблизним наближенням.
Бартош Фірін

13

Я завжди розумів, що наведене нижче дасть мені найшвидший час відгуку.

SELECT COUNT(1) FROM ... WHERE ...

1
Хіба не ВИБІР 1 З ... ДЕ ... був би ще швидшим?
патрік

3
@patrick - SELECT 1 ...поверне стільки ж рядків, скільки WHEREі LIMITзапитає, і всі вони будуть "1".
Рік Джеймс

1
show table status like '<TABLE NAME>' Це буде набагато швидше.
глибокий

@deep - але не актуально, якщо у вас є WHEREпункт. А для InnoDB - це лише оцінка.
Рік Джеймс

@RickJames так правда!
глибокий

6

Якщо вам потрібно отримати кількість всього набору результатів, ви можете скористатися наступним підходом:

SELECT SQL_CALC_FOUND_ROWS * FROM table_name LIMIT 5;
SELECT FOUND_ROWS();

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

Виконання цих двох запитів корисно для створення сторінок для отримання підсумків, але не особливо для використання WHEREпропозицій.


Інтестування. Це працює в найбільш поширених системах баз даних? MySQL, Postgres, SQLite ...?
Франц

4
Це насправді часто не швидше, ніж використання COUNT (*) взагалі. Див stackoverflow.com/questions/186588 / ...
toon81

2
Ви повинні бути ДУЖЕ обережними, використовуючи цю функцію. Його необачне використання колись призвело до того, що все наше виробниче середовище зупинилось. Це ДУЖЕ багато ресурсів, тому використовуйте обережно.
Janis Peisenieks

6

Я зробив кілька орієнтирів для порівняння часу виконання COUNT(*)vs COUNT(id)(id - це первинний ключ таблиці - індексований).

Кількість випробувань: 10 * 1000 запитів

Результати: COUNT(*)швидше 7%

ПОГЛЯД ГРАФІЯ: орієнтир

Моя порада: SELECT COUNT(*) FROM table


1
FYI є також поширений спосіб порахувати COUNT(1), було б цікаво подивитися деякі орієнтири там ...
Sliq

4

Спробуйте це:

SELECT
    table_rows "Rows Count"
FROM
    information_schema.tables
WHERE
    table_name="Table_Name"
AND
    table_schema="Database_Name";

@lepe пробач. Я мав на увазі, це дуже приємно, якщо хтось, хто робив зворотний позов, дав пояснення, чому він / вона це робить, тому кожен може щось дізнатися про це.
bayuah

1
Це дасть вам приблизний відповідь швидко. Якщо вам потрібна точна відповідь, вам потрібно виконати select count(*) from table_nameчи щось інше. dba.stackexchange.com/questions/151769 / ...
Programster

@Programster Дякую Краще, ніж залишати мене в темряві майже рік.
bayuah

1
@bayuah Я не впевнений, що ти мав на увазі під своїм останнім коментарем. Я можу лише припустити, що ви думаєте, що я є тим, хто озвучив вашу відповідь, а це не я.
Programster

1
@Programster Ні, вибачте, я цього не мав на увазі. Я мав на увазі дякую за ваше пояснення, тому я можу припустити, що, можливо, думав Давотер, коли він це зробив.
bayuah

3

Можливо, ви захочете подумати про те, як робити SELECT max(Id) - min(Id) + 1. Це буде працювати лише в тому випадку, якщо ваші ідентифікатори є послідовними та рядки не видаляються. Однак це дуже швидко.


3
Будьте уважні: сервери іноді використовують значення автоматичного збільшення> 1 (з резервного копіювання), тому це рішення добре, але спершу слід перевірити конфігурацію БД.
Олексій

1

EXPLAIN SELECT id FROM ....зробив трюк для мене. і я міг бачити кількість рядків під rowsстовпцем результату.


0

Я обробляв таблиці для німецького уряду з часом 60 мільйонів записів.

І нам потрібно було знати багато разів загальні рядки.

Тому ми, програмісти баз даних, вирішили, що в кожну таблицю записується один завжди запис, в якому зберігається загальна кількість записів. Ми оновили це число, залежно від рядків INSERT або DELETE.

Ми спробували всі інші способи. Це, безумовно, найшвидший спосіб.


1
і які дані про те, як ви оновили цей рядок? Що означає хоч і несправний дизайн до столу, де всі ряди вимагатимуть витраченого int, щоб прийти разом для їзди.
Дрю

5
Так, це справді дурний ха-ха. З кожним запитом ви повинні ігнорувати перший рядок. Я просто створив би таблицю підсумків і заповнив її на основі тригера. Таблиця користувачів на вставці, оновлення таблиці підсумків. Таблиця користувачів на видалення, оновлення таблиці підсумків.
HTMLGuy

-1

Оператор count (*) з умовою, де умова в первинному ключі повертає кількість рядків набагато швидше, уникаючи сканування повного столу.

SELECT COUNT(*) FROM ... WHERE <PRIMARY_KEY> IS NOT NULL;

Це було набагато швидше для мене

SELECT COUNT(*) FROM ...
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.