Количество (*) против Количество (1) - SQL Server

Просто интересно, кто-нибудь из вас, ребята, использует Count(1) вместо Count(*) и есть ли заметная разница в производительности или это просто унаследованная привычка, появившаяся в прошлых днях?

(Конкретная база данных - SQL Server 2005.)

Ответ 1

Нет никакой разницы.

Причина:

Книги в режиме онлайн говорит "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

Аналогичный вопрос по DBA.SE.

Изменить, декабрь 2011

COUNT(*) упоминается специально в ANSI-92 (найдите "Scalar expressions 125" )

Случай:

a) Если задано COUNT (*), то результатом будет мощность T.

То есть стандарт ANSI распознает его как кровотечение, очевидно, что вы имеете в виду. COUNT(1) был оптимизирован поставщиками РСУБД из-за этого суеверия. В противном случае он будет оцениваться согласно ANSI

b) В противном случае пусть TX - таблица с одним столбцом, которая является               результат применения выражения < value выражение > к каждой строке T               и устранение нулевых значений. Если одно или несколько нулевых значений               устраняется, то возникает условие завершения: предупреждение -

Ответ 2

В 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(*))

Ответ 3

Очевидно, что COUNT (*) и COUNT (1) будут всегда возвращать тот же результат. Поэтому, если бы кто-то был медленнее, чем другой, это было бы эффективно из-за ошибки оптимизатора. Поскольку обе формы используются очень часто в запросах, для СУБД нет смысла допускать, чтобы такая ошибка оставалась незафиксированной. Следовательно, вы обнаружите, что производительность обеих форм (вероятно) одинакова во всех основных СУБД SQL.

Ответ 4

В стандарте SQL-92 COUNT(*) конкретно означает "мощность табличного выражения" (может быть базовая таблица, "VIEW", производная таблица, CTE и т.д.).

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

В том же ключе COUNT(*) может быть легко отображен человеческим кодером, знакомым с SQL-стандартами, полезным навыком при работе с несколькими предложениями поставщика SQL.

Кроме того, в специальном случае SELECT COUNT(*) FROM MyPersistedTable; мышление представляет собой СУБД, вероятно, будет содержать статистику для мощности таблицы.

Поэтому, поскольку COUNT(1) и COUNT(*) являются семантически эквивалентными, я использую COUNT(*).

Ответ 5

COUNT(*) и COUNT(1) одинаковы в случае результата и производительности.

Ответ 6

Я работаю в команде 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

Ответ 7

Я бы ожидал, что оптимизатор обеспечит отсутствие реальной разницы вне странных краевых случаев.

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

Тем не менее, я всегда использовал COUNT(*).

Ответ 8

Поскольку этот вопрос возникает снова и снова, вот еще один ответ. Я надеюсь добавить что-то для новичков, интересующихся "лучшей практикой" здесь.

SELECT COUNT(*) FROM something подсчитывает записи, что является легкой задачей.

SELECT COUNT(1) FROM something извлекает 1 за запись, а не подсчитывает 1s, которые не являются нулевыми, что по существу подсчитывает записи, только более сложные.

Сказав это: Хорошие dbms заметили, что второй оператор приведет к тому же счету, что и первый оператор, и переконфигурирует его соответственно, чтобы не делать ненужной работы. Таким образом, обычно оба утверждения приводят к одному и тому же плану выполнения и занимают одинаковый промежуток времени.

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

Ответ 9

Я проверил быстрый тест на 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 перед запуском обоих прогонов.

Ответ 10

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 мс.

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

Ответ 11

Существует статья, показывающая, что 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 по той же теме, без упоминания базы данных, были закрыты и помечены как дублированные из этого ответа.

Ответ 12

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

Ответ 13

Во всех СУБД оба способа подсчета эквивалентны с точки зрения того, какой результат они производят. Что касается производительности, я не заметил каких-либо различий в производительности SQL Server, но, возможно, стоит отметить, что некоторые СУБД, , например, PostgreSQL 11 имеет менее оптимальные реализации для COUNT(1), поскольку они проверяют обнуляемость выражения аргумента, как можно увидеть в этом посте.

Я обнаружил, что при выполнении 1М строк разница в производительности составляет 10%:

-- Faster
SELECT COUNT(*) FROM t;

-- 10% slower
SELECT COUNT(1) FROM t;