Подтвердить что ты не робот

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

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

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

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

Сколько стоит запросов COUNT (*)?

4b9b3361

Ответ 1

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

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

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

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

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

Ответ 2

В целом стоимость стоимости 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, и план выполнения будет примерно таким:

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

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

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

Ответ 3

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

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

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

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

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

Ответ 4

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

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

I.e., если вы используете 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, должен быть способ включить автоматическое пакетное допрос.

Ответ 5

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

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

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

Ответ 6

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

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

Ответ 7

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