Count (*) vs Count (1) - SQL Server

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

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

11 ответов


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

причина:

книги-лайн говорит:"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, тот же план, работы

Edit, Aug 2011

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

Edit, Dec 2011

COUNT(*) упоминается конкретно в в ANSI-92 (ищите "Scalar expressions 125")

корпус:

a) если указан COUNT (*), то результатом является мощность T.

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

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


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


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

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

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


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

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

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

сказав это: хорошая СУБД заметит, что второй оператор приведет к тому же количеству, что и первый заявление и заново интерпретировать его соответствующим образом, а не делать ненужную работу. Поэтому обычно оба оператора приводят к одному и тому же плану выполнения и занимают одинаковое количество времени.

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


Я провел быстрый тест на SQL Server 2012 на 8 ГБ оперативной памяти hyper-V. Вы можете сами увидеть результаты. Во время выполнения этих тестов я не запускал никаких других оконных приложений, кроме 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:
Время процессора = 31 МС, прошедшее время = 36 МС.

select count(*) from MyTable (nolock) -- table containing 1 million records. 

время выполнения SQL Server:
Время процессора = 46 МС, прошедшее время = 37 МС.

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


есть статьи показывал, что COUNT(1) on Оракул - это просто псевдоним COUNT(*) С доказательство об этом.

я процитирую некоторые части:

есть часть программного обеспечения базы данных, которая называется " Оптимизатор", который определяется в официальной документации как "Встроенное программное обеспечение базы данных, которое определяет наиболее эффективный способ выполните инструкцию 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 oracle должна была вычислить (1) для каждой строки как функцию перед детерминированным и Недетерминированные существуют.

Итак, два десятилетия назад count (*) был быстрее

для других баз данных, как Sql Server, он должен быть исследован индивидуально для каждого из них.

Я знаю, что этот вопрос специфичен для Sql Server, но другие вопросы по SO по той же теме, без упоминания базы данных, был закрыт и помечен как дублированный из этого ответа.


легкий для того чтобы демо-счет(*) против графа()--

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