Просто интересно, кто-нибудь из вас, ребята, использует Count(1)
вместо Count(*)
и есть ли заметная разница в производительности или это просто унаследованная привычка, появившаяся в прошлых днях?
(Конкретная база данных - SQL Server 2005
.)
Просто интересно, кто-нибудь из вас, ребята, использует Count(1)
вместо Count(*)
и есть ли заметная разница в производительности или это просто унаследованная привычка, появившаяся в прошлых днях?
(Конкретная база данных - SQL Server 2005
.)
Нет никакой разницы.
Причина:
Книги в режиме онлайн говорит "
COUNT ( { [ [ ALL | DISTINCT ] expression ] | * } )
"
"1" - это ненулевое выражение: так что оно равно COUNT(*)
.
Оптимизатор распознает его для чего: тривиальный.
То же, что и EXISTS (SELECT * ...
или EXISTS (SELECT 1 ...
Пример:
SELECT COUNT(1) FROM dbo.tab800krows
SELECT COUNT(1),FKID FROM dbo.tab800krows GROUP BY FKID
SELECT COUNT(*) FROM dbo.tab800krows
SELECT COUNT(*),FKID FROM dbo.tab800krows GROUP BY FKID
Тот же самый IO, тот же план, работы
Изменить, август 2011
Изменить, декабрь 2011
COUNT(*)
упоминается специально в ANSI-92 (найдите "Scalar expressions 125
" )
Случай:
a) Если задано COUNT (*), то результатом будет мощность T.
То есть стандарт ANSI распознает его как кровотечение, очевидно, что вы имеете в виду. COUNT(1)
был оптимизирован поставщиками РСУБД из-за этого суеверия. В противном случае он будет оцениваться согласно ANSI
b) В противном случае пусть TX - таблица с одним столбцом, которая является результат применения выражения < value выражение > к каждой строке T и устранение нулевых значений. Если одно или несколько нулевых значений устраняется, то возникает условие завершения: предупреждение -
В SQL Server эти инструкции дают те же планы.
Вопреки распространенному мнению, в Oracle они тоже делают.
SYS_GUID()
в Oracle - довольно интенсивная вычислительная функция.
В моей тестовой базе данных t_even
находится таблица с 1,000,000
строками
Этот запрос:
SELECT COUNT(SYS_GUID())
FROM t_even
работает за 48
секунды, так как функция должна оценивать каждый возвращаемый SYS_GUID()
, чтобы убедиться, что это не NULL
.
Однако этот запрос:
SELECT COUNT(*)
FROM (
SELECT SYS_GUID()
FROM t_even
)
работает за 2
секунд, так как он даже не пытается оценить SYS_GUID()
(несмотря на то, что *
является аргументом COUNT(*)
)
Очевидно, что COUNT (*) и COUNT (1) будут всегда возвращать тот же результат. Поэтому, если бы кто-то был медленнее, чем другой, это было бы эффективно из-за ошибки оптимизатора. Поскольку обе формы используются очень часто в запросах, для СУБД нет смысла допускать, чтобы такая ошибка оставалась незафиксированной. Следовательно, вы обнаружите, что производительность обеих форм (вероятно) одинакова во всех основных СУБД SQL.
В стандарте SQL-92 COUNT(*)
конкретно означает "мощность табличного выражения" (может быть базовая таблица, "VIEW", производная таблица, CTE и т.д.).
Я думаю, идея заключалась в том, что COUNT(*)
легко разобрать. Использование любого другого выражения требует, чтобы синтаксический анализатор обеспечивал, чтобы он не ссылался ни на какие столбцы (COUNT('a')
где a
является литералом, а COUNT(a)
, где a
- столбец, может давать разные результаты).
В том же ключе COUNT(*)
может быть легко отображен человеческим кодером, знакомым с SQL-стандартами, полезным навыком при работе с несколькими предложениями поставщика SQL.
Кроме того, в специальном случае SELECT COUNT(*) FROM MyPersistedTable;
мышление представляет собой СУБД, вероятно, будет содержать статистику для мощности таблицы.
Поэтому, поскольку COUNT(1)
и COUNT(*)
являются семантически эквивалентными, я использую COUNT(*)
.
COUNT(*)
и COUNT(1)
одинаковы в случае результата и производительности.
Я работаю в команде SQL Server и, надеюсь, могу уточнить некоторые моменты в этой теме (я раньше этого не видел, поэтому мне жаль, что команда разработчиков не сделала этого раньше).
Во-первых, между select count(1) from table
и select count(*) from table
нет семантической разницы. Они возвращают одинаковые результаты во всех случаях (и это ошибка, если нет). Как отмечено в других ответах, select count(column) from table
семантически отличается и не всегда возвращает те же результаты, что и count(*)
.
Во-вторых, в отношении производительности в SQL Server (и в SQL Azure) могут иметь значение два аспекта: работа во время компиляции и работа во время выполнения. Работа во время компиляции - это незначительный объем дополнительной работы в текущей реализации. В некоторых случаях расширение * распространяется на все столбцы, после чего выводится обратно до 1 столбца, выводимого из-за того, как некоторые из внутренних операций работают при связывании и оптимизации. Я сомневаюсь, что это проявилось бы в любом измеримом тесте и, вероятно, затерялось бы в шуме всех других вещей, которые происходят под прикрытием (таких как автостатистика, сеансы xevent, накладные расходы хранилища запросов, триггеры и т.д.). Это может быть несколько тысяч дополнительных инструкций процессора. Таким образом, count (1) выполняет чуть меньше работы во время компиляции (что обычно происходит один раз, и план кэшируется в нескольких последующих выполнениях). Что касается времени выполнения, при условии, что планы одинаковы, не должно быть никакой измеримой разницы. (Один из предыдущих примеров показывает разницу - скорее всего, из-за других факторов на машине, если план такой же).
Что касается того, как план может быть другим. Это крайне маловероятно, но это потенциально возможно в архитектуре текущего оптимизатора. Оптимизатор SQL Server работает как поисковая программа (представьте: компьютерная программа, играющая в шахматы, ищущая различные альтернативы для разных частей запроса и оценивающая альтернативы, чтобы найти самый дешевый план за разумное время). Этот поиск имеет несколько ограничений на то, как он работает, чтобы компиляция запросов заканчивалась в разумные сроки. Для запросов, выходящих за рамки тривиальных, существуют фазы поиска, и они имеют дело с траншами запросов, основанными на том, насколько дорогостоящим оптимизатор считает, что запрос потенциально может выполняться. Существует 3 основных этапа поиска, и каждый этап может использовать более агрессивную (дорогую) эвристику, пытаясь найти более дешевый план, чем любое предыдущее решение. В конце концов, в конце каждой фазы происходит процесс принятия решения, который пытается определить, должен ли он вернуть план, который он нашел до сих пор, или должен продолжать поиск. В этом процессе используется общее время, затраченное на данный момент, в сравнении с оценочной стоимостью лучшего плана, найденного на данный момент. Таким образом, на разных машинах с разными скоростями ЦП возможно (хотя и редко) получить разные планы из-за тайм-аута на более ранней стадии с планом по сравнению с переходом на следующую фазу поиска. Есть также несколько похожих сценариев, связанных с тайм-аутом последней фазы и потенциальным исчерпанием памяти для очень и очень дорогих запросов, которые занимают всю память на машине (обычно это не проблема для 64-битных систем, но это было более серьезной проблемой). назад на 32-битных серверах). В конечном итоге, если вы получите другой план, производительность во время выполнения будет отличаться. Я не думаю, что маловероятно, что разница во времени компиляции НИКОГДА приведет к возникновению любого из этих условий.
Net-net: Пожалуйста, используйте любой из двух вариантов, который вам нужен, поскольку в практической форме это не имеет значения. (Честно говоря, есть гораздо более серьезные факторы, влияющие на производительность в SQL).
Надеюсь, это поможет. Я написал главу книги о том, как работает оптимизатор, но я не знаю, уместно ли размещать его здесь (так как я все еще верю, что я получаю от него небольшие гонорары). Таким образом, вместо публикации я опубликую ссылку на доклад, который я дал на SQLBits в Великобритании о том, как оптимизатор работает на высоком уровне, чтобы вы могли увидеть различные основные фазы поиска более подробно, если хотите чтобы узнать об этом. Вот ссылка на видео: https://sqlbits.com/Sessions/Event6/inside_the_sql_server_query_optimizer
Я бы ожидал, что оптимизатор обеспечит отсутствие реальной разницы вне странных краевых случаев.
Как и во всем, единственный реальный способ рассказать - это измерить ваши конкретные случаи.
Тем не менее, я всегда использовал COUNT(*)
.
Поскольку этот вопрос возникает снова и снова, вот еще один ответ. Я надеюсь добавить что-то для новичков, интересующихся "лучшей практикой" здесь.
SELECT COUNT(*) FROM something
подсчитывает записи, что является легкой задачей.
SELECT COUNT(1) FROM something
извлекает 1 за запись, а не подсчитывает 1s, которые не являются нулевыми, что по существу подсчитывает записи, только более сложные.
Сказав это: Хорошие dbms заметили, что второй оператор приведет к тому же счету, что и первый оператор, и переконфигурирует его соответственно, чтобы не делать ненужной работы. Таким образом, обычно оба утверждения приводят к одному и тому же плану выполнения и занимают одинаковый промежуток времени.
Однако с точки зрения удобочитаемости вы должны использовать первый оператор. Вы хотите подсчитывать записи, поэтому учитывайте записи, а не выражения. Используйте COUNT (выражение) только тогда, когда вы хотите подсчитать ненулевые вхождения чего-либо.
Я проверил быстрый тест на SQL Server 2012 на 8-Гбайт оперативной памяти. Вы можете сами увидеть результаты. При выполнении этих тестов я не запускал никаких других оконных приложений, кроме SQL Server Management Studio.
Моя таблица:
CREATE TABLE [dbo].[employee](
[Id] [bigint] IDENTITY(1,1) NOT NULL,
[Name] [nvarchar](50) NOT NULL,
CONSTRAINT [PK_employee] PRIMARY KEY CLUSTERED
(
[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
Общее количество записей в таблице Employee
: 178090131 (~ 178 миллионов строк)
Первый запрос:
Set Statistics Time On
Go
Select Count(*) From Employee
Go
Set Statistics Time Off
Go
Результат первого запроса:
SQL Server parse and compile time:
CPU time = 0 ms, elapsed time = 35 ms.
(1 row(s) affected)
SQL Server Execution Times:
CPU time = 10766 ms, elapsed time = 70265 ms.
SQL Server parse and compile time:
CPU time = 0 ms, elapsed time = 0 ms.
Второй запрос:
Set Statistics Time On
Go
Select Count(1) From Employee
Go
Set Statistics Time Off
Go
Результат второго запроса:
SQL Server parse and compile time:
CPU time = 14 ms, elapsed time = 14 ms.
(1 row(s) affected)
SQL Server Execution Times:
CPU time = 11031 ms, elapsed time = 70182 ms.
SQL Server parse and compile time:
CPU time = 0 ms, elapsed time = 0 ms.
Вы можете заметить, что существует разница в 83 (= 70265 - 70182) миллисекунды, которую легко можно отнести к точному состоянию системы во время выполнения запросов. Также я сделал один прогон, поэтому эта разница станет более точной, если я сделаю несколько прогонов и сделаю некоторое усреднение. Если для такого огромного набора данных разница будет меньше 100 миллисекунд, то мы можем легко заключить, что у двух запросов нет какой-либо разницы в производительности, проявляемой SQL Server Engine.
Примечание. ОЗУ достигает почти 100% использования в обоих прогонах. Я перезапустил службу SQL Server перед запуском обоих прогонов.
SET STATISTICS TIME ON
select count(1) from MyTable (nolock) -- table containing 1 million records.
Время выполнения SQL Server:
Время CPU = 31 мс, прошедшее время = 36 мс.
select count(*) from MyTable (nolock) -- table containing 1 million records.
Время выполнения SQL Server:
Время CPU = 46 мс, прошедшее время = 37 мс.
Я запускал эти сотни раз, очищая кеш каждый раз. Результаты меняются время от времени, так как загрузка сервера меняется, но почти всегда счетчик (*) имеет более высокое время процессора.
Существует статья, показывающая, что COUNT(1)
в Oracle является просто псевдонимом COUNT(*)
, с доказательством об этом.
Я процитирую некоторые части:
Существует часть программного обеспечения базы данных, которая называется Optimizer ', который определяется в официальной документации как Встроенное программное обеспечение базы данных, которое определяет наиболее эффективный способ выполнить инструкцию SQL '.
Один из компонентов оптимизатора называется "трансформатор", чья роль состоит в том, чтобы определить, выгодно ли переписать исходный оператор SQL в семантически эквивалентный оператор SQL это может быть более эффективным.
Хотите посмотреть, что делает оптимизатор при написании запроса? используя COUNT (1)?
Имея пользователя с привилегией ALTER SESSION
, вы можете установить tracefile_identifier
, включить трассировку оптимизатора и запустить выбор COUNT(1)
, например: SELECT /* test-1 */ COUNT(1) FROM employees;
.
После этого вам нужно локализовать файлы трассировки, что можно сделать с помощью SELECT VALUE FROM V$DIAG_INFO WHERE NAME = 'Diag Trace';
. Позже вы найдете файл:
SELECT COUNT(*) 'COUNT(1)' FROM 'COURSE'.'EMPLOYEES' 'EMPLOYEES'
Как видите, это просто псевдоним для COUNT(*)
.
Еще один важный комментарий: COUNT(*)
был действительно быстрее два десятилетия назад в Oracle, до Oracle 7.3:
Count (1) был переписан в count (*) начиная с 7.3, потому что Oracle как Автоматически настраивать мифические высказывания. В более раннем Oracle7 оракулу пришлось выполнить оценку (1) для каждой строки, как функцию, перед DETERMINISTIC и НЕ ДЕТЕРМИНИСТИЧЕСКИЕ существуют.
Итак, два десятилетия назад count (*) был быстрее
Для других баз данных, таких как Sql Server, их следует исследовать отдельно для каждой.
Я знаю, что этот вопрос специфичен для Sql Server, но другие вопросы по SO по той же теме, без упоминания базы данных, были закрыты и помечены как дублированные из этого ответа.
COUNT (1) существенно не отличается от COUNT (*), если вообще. Что касается вопроса о СЧЕТАХ НЕДОПУСТИМЫХ КОЛОНН, это может быть просто продемонстрировать различия между COUNT (*) и COUNT (& lt; некоторые col & gt;) -
USE tempdb;
GO
IF OBJECT_ID( N'dbo.Blitzen', N'U') IS NOT NULL DROP TABLE dbo.Blitzen;
GO
CREATE TABLE dbo.Blitzen (ID INT NULL, Somelala CHAR(1) NULL);
INSERT dbo.Blitzen SELECT 1, 'A';
INSERT dbo.Blitzen SELECT NULL, NULL;
INSERT dbo.Blitzen SELECT NULL, 'A';
INSERT dbo.Blitzen SELECT 1, NULL;
SELECT COUNT(*), COUNT(1), COUNT(ID), COUNT(Somelala) FROM dbo.Blitzen;
GO
DROP TABLE dbo.Blitzen;
GO
Во всех СУБД оба способа подсчета эквивалентны с точки зрения того, какой результат они производят. Что касается производительности, я не заметил каких-либо различий в производительности SQL Server, но, возможно, стоит отметить, что некоторые СУБД, , например, PostgreSQL 11 имеет менее оптимальные реализации для COUNT(1)
, поскольку они проверяют обнуляемость выражения аргумента, как можно увидеть в этом посте.
Я обнаружил, что при выполнении 1М строк разница в производительности составляет 10%:
-- Faster
SELECT COUNT(*) FROM t;
-- 10% slower
SELECT COUNT(1) FROM t;