Является ли использование COUNT(*) или SELECT * хорошей идеей?
Я слышал несколько раз, что вы не должны выступать COUNT(*)
или же SELECT *
по соображениям производительности, но не смог найти дополнительную информацию об этом.
Я могу себе представить, что тогда база данных использует все столбцы для действия, что может привести к впечатляющей потере производительности, но я не уверен в этом. У кого-нибудь есть дополнительная информация по теме?
8 ответов
1. На счет (*) против счет (что-то еще)
SQL декларативен в том смысле, что вы указываете, что вы хотите. Это отличается от указания, как получить то, что вы хотите. Это означает, что ядро базы данных может свободно реализовать ваш запрос так, как он считает наиболее эффективным. Многие оптимизаторы базы данных переписывают ваш запрос на менее дорогостоящую альтернативу (если такой план доступен).
Учитывая следующую таблицу:
table(
pk not null
,color not null
,nullable null
,unique(pk)
,index(color)
);
...все следующее функционально эквивалентно (из-за механики count и null):
1) select count(*) from table;
2) select count(1) from table;
3) select count(pk) from table;
4) select count(color) from table;
Независимо от того, какую форму вы используете, оптимизатор может переписать запрос в другую форму, если она более эффективна. (Опять же, не все оптимизаторы достаточно сложны, чтобы сделать это). Уникальный индекс (pk) будет меньше (занято байтов), чем вся таблица. Поэтому было бы более эффективно рассчитывать количество записей индекса, а не сканировать всю таблицу. В Oracle у нас есть растровые индексы, которые также сжимают повторяющиеся строки. Если бы мы использовали такой индекс для цветного столбца, он, вероятно, был бы наименьшим индексом для сканирования. Oracle также поддерживает сжатие таблиц, что в некоторых случаях делает физическую таблицу меньше, чем составной индекс.
1. TL; DR; Ваши конкретные базы данных будут иметь свой собственный набор инструментов, который позволяет различные правила переписывания и в свою очередь планы выполнения. Это делает вопрос несколько бесполезным (если мы не говорим о конкретном выпуске конкретной базы данных). рекомендую COUNT(*)
во всех случаях, потому что это требует наименьших когнитивных усилий, чтобы понять.
2. При выборе a, b, c и select *
Есть очень мало действительных применений SELECT *
в коде вы пишете и запускаете в производство. Представьте себе таблицу, которая содержит Blu-Ray фильмы (да, фильмы хранятся в виде BLOB-объектов в этой таблице). Итак, вы собрали свой слой абстракции awesomesauce и положили SELECT * FROM movies where id = ?
в getMovies(movie_id)
метод. Я воздержусь от объяснения, почему SELECT name FROM movies
будет транспортироваться по сети чуть быстрее. Конечно, в большинстве реальных случаев это не окажет заметного влияния.
Последнее, что влияет на производительность, - это то, что когда все столбцы (выбранные, отфильтрованные) в вашем запросе существуют как индекс (называемый индексом покрытия), базе данных вообще не нужно прикасаться к таблице. Это может быть полностью решено только путем сканирования индекса. Выбрав все столбцы, вы удаляете эту опцию из оптимизатора.
Еще одна вещь о SELECT *
что гораздо более серьезно, чем что-либо, состоит в том, что он создает неявную зависимость от конкретной физической структуры таблицы. Позволь мне объяснить. Рассмотрим следующие таблицы:
table T1(name, id)
table T2(name, id)
Следующее утверждение...
insert into t1 select * from t2;
... сломается или даст другой результат, если произойдет любое из следующего:
- Любой из столбцов таблиц переставляется, например, T1 (id, name)
- T1 получает дополнительный не нулевой столбец
- Т2 получает еще один столбец
2. TL;DR; По возможности, явно указывайте нужные столбцы (в конце концов, вам придется делать это в любом случае). Кроме того, выбор меньшего количества столбцов выполняется быстрее, чем выбор большего количества столбцов. Возможным побочным эффектом явного выбора является то, что он дает большую свободу оптимизатору.
COUNT(*)
отличается от COUNT(column1)
!COUNT(*)
возвращает количество записей и НЕ использует больше ресурсов, в то время как COUNT(column1)
подсчитывает количество записей, где column1 не является нулевым.
За SELECT
, это отличается. SELECT *
конечно, запросит больше данных.
Когда используешь count(*)
*
не означает "все поля". С помощью count(field)
будет подсчитывать все ненулевые значения в поле, но count(*)
всегда будет считать все записи, даже если все поля во всех записях равны нулю, поэтому не нужно проверять данные в полях вообще.
С помощью select *
означает, что вы почти всегда возвращаете больше данных, чем собираетесь использовать, что, конечно, является пустой тратой. Однако, возможно, более серьезной является проблема сопровождения; если вы добавите поля в таблицу, ваш запрос также вернет их. Это может означать, что запись становится слишком большой для размещения в буфере, что приводит к сообщению об ошибке.
Не путайте * в "COUNT(*)" с * в "SELECT * ". Они совершенно не связаны, но иногда путаются, потому что это такой странный синтаксис. В использовании COUNT (*) нет ничего плохого, что означает "считать строки".
SELECT * означает "выбрать все столбцы". Как правило, это плохая практика, поскольку она тесно связывает ваш код со схемой базы данных. Это означает, что когда вы меняете таблицу, вам, вероятно, придется изменить код, даже если он не был затронут. Это увеличивает влияние любого изменения схемы.
SELECT * может также вызвать неоптимальный план запроса. Либо потому, что вам действительно не нужны все столбцы, либо потому, что это заставляет СУБД выполнить дополнительный поиск во время выполнения, чтобы получить список столбцов.
Когда используешь SELECT *
это может иметь удар по производительности. Приложения, которые используют SELECT *
Синтаксис, когда им нужно всего лишь несколько столбцов, передает больше данных по сети, чем им нужно, что является расточительным.
Кроме того, по крайней мере в Microsoft SQL Server, при использовании SELECT *
в представлении, а затем добавьте столбец к базовой таблице. Заголовки столбцов и данные, возвращаемые представлением, не соответствуют друг другу после определенных изменений! Смотрите мой блог для получения более подробной информации об этой конкретной проблеме.
Абсолютно верно, что "*" - это "все столбцы". И вы правы в том, что если у вас таблица с невероятным количеством столбцов (скажем, 100+), такие запросы могут быть плохими с точки зрения эффективности.
Я считаю, что лучшим решением является создание представлений базы данных, предварительно отфильтровавших количество записей, выделенных в операции подсчета, поэтому влияние на производительность не представляет большой проблемы, поскольку представления могут кэшироваться.
С другой стороны, кажется, что при возврате записей следует избегать оператора "*", и гораздо лучше выбрать поля, которые вам действительно нужно использовать в каком-либо бизнесе.
В зависимости от размера базы данных, от того, насколько она становится неэффективной, самый простой способ описать это так:
когда вы специально делаете:
SELECT column1,column2,column3 FROM table1
Mysql точно знает, какие столбцы он ищет, но когда вы делаете
SELECT * FROM table1
Mysql не знает столбцы, которые вы хотите, он знает, что вы хотите все их, но не имена, поэтому он должен выполнить дополнительные задачи, которые анализируют таблицу, чтобы обнаружить столбцы, что приводит к использованию ресурсов.
В случае COUNT(*)
это зависит от базы данных и ее версии. Например в современных версиях MS SQL
это не имеет значения [источник нужен].
Так что лучший подход в случае COUNT(*)
это измерить это.
С помощью SELECT *
действительно плохая идея. *
означает чтение всех столбцов, которые могут быть интенсивного ввода-вывода и работы в сети (особенно для различного типа CHAR
колонны). Более того - довольно редко нужны все колонки.