Количество (*) действительно дорого?

У меня есть страница, где у меня есть 4 вкладки, отображающие 4 различных отчета, базирующиеся от различных таблиц.

Я получаю количество строки каждой таблицы с помощью a select count(*) from <table> запросите и отобразите количество строк, доступных в каждой таблице на вкладках. В результате каждая обратная передача страницы вызывает 5 count(*) запросы, которые будут выполняться (4 для получения количеств и 1 для разбиения на страницы) и 1 запрос для получения содержания отчета.

Теперь мой вопрос: count(*) действительно дорогие запросы - я должен провести подсчет строки (по крайней мере, те, которые отображены на вкладке) в состоянии отображения страницы вместо того, чтобы запросить многократно?

Насколько дорогой КОЛИЧЕСТВО (*) запросы?

18
задан Ruben Bartelink 27 April 2010 в 10:57
поделиться

7 ответов

Вам необходимо прикрепить SQL Profiler или профилировщик уровня приложения, например L2SProf и посмотрите на реальные затраты на запрос в вашем контексте, прежде чем:

  • угадывать, в чем проблема, и пытаться определить вероятные преимущества потенциального решения

  • , позволяя другим угадывать за вас в да интернета - есть много дезинформации без цитат о , включая в эту ветку (но не в этот пост: P)

Когда вы это сделаете, станет ясно, какой подход лучше всего - то есть, доминирует ли SELECT COUNT над вещами или нет и т. д.

. Сделав это, вы также узнаете, оказали ли какие-либо изменения, которые вы решите сделать, положительное или отрицательное влияние.

8
ответ дан 30 November 2019 в 09:03
поделиться

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

В простых случаях, когда вы имеете дело с одной таблицей, часто используются специальные оптимизации, чтобы сделать такую ​​операцию дешевой. Например, выполнение COUNT (*) без условий WHERE из одной таблицы MyISAM в MySQL - это происходит мгновенно, поскольку оно сохраняется в метаданных.

Например, давайте рассмотрим два запроса:

SELECT  COUNT(*)
FROM    largeTableA a

Поскольку каждая запись удовлетворяет запросу, стоимость COUNT (*) пропорциональна количеству записей в таблице (т. Е. Пропорциональна тому, что она возвращает) (Предполагая, что ему нужно посетить строки и нет специальной оптимизации для его обработки)

SELECT  COUNT(*)
FROM    largeTableA a
JOIN    largeTableB b
ON      a.id = b.id

В этом случае механизм, скорее всего, будет использовать HASH JOIN , и план выполнения будет чем-то примерно так:

  1. Создайте хеш-таблицу на меньшей из таблиц
  2. Сканируйте большую таблицу, просматривая каждую запись в хеш-таблице
  3. Подсчитывайте совпадения по мере их поступления.

В этом случае накладные расходы COUNT (*) (шаг 3) будут незначительными, а время запроса будет полностью определено шагами 1 и 2, то есть построением хеш-таблицы и ее поиском. . Для такого запроса время будет O (a + b) : на самом деле оно не зависит от количества совпадений.

Однако, если есть индексы на a.id и b.id , можно выбрать MERGE JOIN , и время COUNT (*) будет снова пропорционально количеству совпадений, поскольку поиск по индексу будет выполняться после каждого совпадения .

8
ответ дан 30 November 2019 в 09:03
поделиться

Если страница становится медленной, вы можете обратить внимание на то, чтобы минимизировать количество обращений к базе данных, если это вообще возможно. Даже если ваши COUNT (*) запросы имеют значение O (1), если вы делаете их достаточно, это, безусловно, может замедлить работу.

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

То есть, если вы используете ADO.NET, сделайте что-нибудь вроде этого (проверка ошибок опущена для краткости; не зацикленный / нединамический для ясности):

string sql = "SELECT COUNT(*) FROM Table1; SELECT COUNT(*) FROM Table2;"

SqlCommand cmd = new SqlCommand(sql, connection);
SqlDataReader dr = cmd.ExecuteReader();

// Defaults to first result set
dr.Read();
int table1Count = (int)dr[0];

// Move to second result set
dr.NextResult();
dr.Read();
int table2Count = (int)dr[0];

Если вы используете какой-то ORM, например NHibernate, должен быть способ включить автоматическую пакетную обработку запросов.

1
ответ дан 30 November 2019 в 09:03
поделиться

Как говорили другие, COUNT (*) всегда физически считает строки, поэтому, если вы можете сделать это один раз и кэшировать результаты, это, безусловно, предпочтительнее.

Если вы проведете сравнительный анализ и определите, что стоимость незначительна, у вас (в настоящее время) нет проблемы.

Если это окажется слишком дорого для вашего сценария, вы можете сделать свою разбивку на страницы «нечеткой», как в « Отображение от 1 до 500 из примерно 30 000 », используя

SELECT rows FROM sysindexes WHERE id = OBJECT_ID ('sometable') AND indid <2

, который вернет приближение количества строк (это приблизительное значение, поскольку оно не обновляется до CHECKPOINT).

2
ответ дан 30 November 2019 в 09:03
поделиться

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

Судя по звуку, вы каждый раз вызываете операцию загрузки таблицы, которая является медленной, но если она не выполняется заметно медленно или не вызывает каких-либо проблем, не оптимизируйте: преждевременная и ненужная оптимизация может вызвать большие неприятности!

Подсчет индексированного первичного ключа будет намного быстрее, но с учетом стоимости индекса это может не принести никакой пользы.

0
ответ дан 30 November 2019 в 09:03
поделиться

Это зависит от того, что вы делаете с данными в этой таблице. Если они меняются очень часто и вам нужны все они каждый раз, возможно, вы могли бы создать триггер, который заполнит другую таблицу, состоящую только из счетчиков из этой таблицы. Если вам нужно показать эти данные отдельно, возможно, вы могли бы просто выполнить «select count (*) ...» только для одной конкретной таблицы. Это сразу пришло мне в голову, но я уверен, что есть и другие способы ускорить это. Может, данные кеша? :)

0
ответ дан 30 November 2019 в 09:03
поделиться

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

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

0
ответ дан 30 November 2019 в 09:03
поделиться
Другие вопросы по тегам:

Похожие вопросы: