개수 (*) 및 개수 (1)-SQL Server


738

사람들 중 일부를 Count(1)과도하게 사용 하고 Count(*)성능에 눈에 띄는 차이가 있는지 또는 과거의 일에서 제기 된 레거시 습관인지 궁금하십니까?

특정 데이터베이스는 SQL Server 2005입니다.


7
SQL Server는 모르지만 MySQL에서는 차이가 없습니다. 반면에 COUNT (열)는 다릅니다
Greg Greg

118
사실이 아니다. COUNT (SomeColumn)은 SomeColumn에 대해 널이 아닌 값을 포함하는 행 수만 리턴합니다. COUNT (*) 및 COUNT ( 'Foo')는 테이블의 총 행 수를 반환합니다.
Steve Broberg

1
자세한 내용 은 그래프와
Ali Adravi

4
와우 스티브와 여기에 count (*) vs Count (ColumnName)를 모르고 TSQL에 5 년을 보냈습니다. 감사합니다
Harindaka

3
COUNT(*)vs COUNT(1)vs에COUNT(pk) 대한 답변도 주목하십시오. 어느 것이 더 낫습니까? . COUNT(*)vsCOUNT(column-name) 도 있습니다 -어느 것이 더 정확합니까? . 다른 복제본이있을 수 있습니다.
Jonathan Leffler

답변:


598

다른 점이 없다.

이유:

온라인 서적에 " 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 년 8 월 편집

DBA.SE와 비슷한 질문입니다 .

2011 년 12 월 수정

COUNT(*)ANSI-92에 구체적으로 언급되어 있습니다 ( " Scalar expressions 125")

케이스:

a) COUNT (*)가 지정되면 결과는 T의 카디널리티입니다.

즉, ANSI 표준은이를 의미하는 출혈로 인식합니다. 이 미신 때문에COUNT(1) RDBMS 공급 업체가 최적화했습니다 . 그렇지 않으면 ANSI에 따라 평가됩니다

b) 그렇지 않으면 TX가 <value expression>을 T의 각 행에 적용하고 널값을 제거한 결과 인 단일 열 테이블이되게하십시오. 하나 이상의 널값이 제거되면 완료 조건이 발생합니다. warning-


73

SQL Server에서 이러한 명령문은 동일한 계획을 산출합니다.

대중의 의견과는 달리, 오라클에서도 마찬가지입니다.

SYS_GUID() 오라클에서는 계산 집약적 인 기능입니다.

내 테스트 데이터베이스에서 행 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(*)


SYS_GUID()하위 쿼리가 결과를 반환하려면 적어도 (정확히) 한 번 평가해야 합니까?
ass

@asgs : 왜 그렇게 생각하십니까? COUNT(*)의 가치에 어떻게 의존 SYS_GUID하는가?
Quassnoi

지금 당신이 묻는, 나는 확실하지 않다. 나는 COUNT(*)실행 하려고 생각했는데 테이블이 필요하므로 하위 쿼리는 하나의 역할을해야합니다. 그렇지 않으면, 내가있는 방법이 표시되지 않습니다 COUNT(*)의미있는 값을 반환하기를
asgs

1
@asgs : 당신은 무엇을 않습니다 알고 가정 map: 방법은 어떻게 어떻게이 두 식을 참조 할 수 t_even.map(() => sys_guid()).lengtht_even.length항상 같은 값을 반환? 오라클의 옵티마이 저는 map부품 을보고 최적화 할 수있을 정도로 똑똑 합니다.
Quassnoi

1
@ 정확하게 정확합니다. 그냥 약간의 보정 : length아주에 의존하지 않는 어떤 컬렉션은 단지 요소의 수에,로 구성되어 있습니다. 이 숫자가 컬렉션의 메타 데이터 (Oracle 또는 대부분의 다른 최신 RDBMS의 경우가 아니라 오래된 MySQL의 스토리지 엔진 인 MyISAM의 경우)에 저장된 COUNT(*)경우 메타 데이터에서 값을 가져와야합니다.
Quassnoi

65

분명히, COUNT(*)그리고 COUNT(1)것입니다 항상 같은 결과를 반환합니다. 따라서 하나가 다른 것보다 느리면 효과적으로 최적화 버그로 인한 것입니다. 두 형식 모두 쿼리에서 매우 자주 사용되므로 DBMS가 이러한 버그를 수정하지 않은 상태로 유지하는 것은 의미가 없습니다. 따라서 모든 주요 SQL DBMS에서 두 양식의 성능이 동일 할 것입니다.


count (1)이 count (*)보다 느리면 버그로 간주하지 않습니다. dbms에 1을 생성하고 널이 아닌 것을 계산하도록 요청하면 예, 레코드 수로 줄어 들지만 dbms가 작성하고 우회하는 모든 넌센스를 감지 할 수는 없습니다.
Thorsten Kettner

1
글쎄, 옵티마이 저는 최적화를 의미하며 카운트에 대해 고려해야 할 두 가지 경우가 있습니다. null 일 수있는 표현식, null이 될 수없는 표현식 : count (1)은 후자에 속하므로 DBMS를 실행할 필요가 없습니다. 질문에 대답하기 위해 1을 "생성"하십시오. (BTW 미학적 이유로 count (*) 이외의 다른 것은 사용하지 않을 것입니다.)
Tony Andrews

46

SQL Server 팀에서 일하고 있으며이 스레드에서 몇 가지 요점을 명확하게 설명 할 수 있습니다 (이전에 본 적이 없으므로 엔지니어링 팀이 이전에 수행하지 않은 것이 유감입니다).

첫째, 사이에 의미 차이가없는 select count(1) from table비교는 select count(*) from table. 그들은 모든 경우에 동일한 결과를 반환합니다 (그렇지 않으면 버그입니다). 다른 답변에서 언급했듯이 select count(column) from table의미 적으로 다르며 항상 같은 결과를 반환하지는 않습니다 count(*).

둘째, 성능과 관련하여 SQL Server (및 SQL Azure)에서 중요한 두 가지 측면, 즉 컴파일 타임 작업과 실행 타임 작업이 있습니다. 컴파일 시간 작업은 현재 구현에서 사소한 추가 작업입니다. 일부 경우에 *를 모든 열로 확장 한 후 일부 내부 조작이 바인딩 및 최적화에서 작동하는 방식으로 인해 출력이 1 열로 다시 축소됩니다. 나는 그것이 측정 가능한 테스트에 나타날 것이라고 의심하고, 커버 아래에서 발생하는 다른 모든 것들 (예 : 자동 통계, xevent 세션, 쿼리 저장소 오버 헤드, 트리거 등)에서 잡음이 손실 될 수 있습니다. 수천 개의 추가 CPU 명령어 일 수 있습니다. 그래서, count (1)은 컴파일하는 동안 약간의 작업을 덜 수행합니다 (일반적으로 한 번 발생하며 계획은 여러 후속 실행에서 캐시 됨). 실행 시간 동안 계획이 동일하다고 가정하면 측정 가능한 차이가 없어야합니다. (이전 예제 중 하나는 차이점을 보여줍니다. 계획이 동일한 경우 시스템의 다른 요인으로 인한 것일 수 있습니다).

계획이 어떻게 다를 수 있는지에 대해. 이러한 상황은 거의 발생하지 않지만 현재 옵티마이 저의 아키텍처에서 가능할 수 있습니다. SQL Server의 옵티마이 저는 검색 프로그램으로 작동합니다 (컴퓨터 프로그램은 쿼리의 다른 부분에 대한 다양한 대안을 통해 체스 검색을 수행하고 합리적인 시간에 가장 저렴한 계획을 찾기 위해 대안을 계산합니다). 이 검색에는 쿼리 컴파일을 적절한 시간 내에 완료하기 위해 작동하는 방식에 몇 가지 제한이 있습니다. 가장 사소한 쿼리 이외의 쿼리에는 검색 단계가 있으며 쿼리가 잠재적으로 쿼리를 실행하는 데 비용이 얼마나 많이 드는지에 따라 쿼리 트랜치를 처리합니다. 3 가지 주요 검색 단계가 있으며 각 단계는 이전 솔루션보다 저렴한 요금제를 찾으려고보다 공격적인 (비싼) 휴리스틱을 실행할 수 있습니다. 궁극적으로 각 단계의 끝에는 지금까지 찾은 계획을 반환해야하는지 또는 계속 검색해야하는지 결정하는 결정 프로세스가 있습니다. 이 프로세스는 지금까지 걸린 총 계획 시간과 지금까지 찾은 최상의 계획의 예상 비용을 사용합니다. 따라서 CPU 속도가 다른 여러 시스템에서 계획이있는 이전 단계에서 다음 검색 단계로 진행하여 이전 단계에서 시간이 초과되어 다른 계획을 얻을 수 있습니다 (드물지만). 마지막 단계의 시간 초과 및 시스템의 모든 메모리를 소비하는 매우 고가의 쿼리에서 잠재적으로 메모리 부족과 관련된 몇 가지 유사한 시나리오도 있습니다 (일반적으로 64 비트의 문제는 아니지만 더 큰 관심 사임) 32 비트 서버로 돌아 가기). 궁극적으로 다른 계획을 세우면 런타임시 성능이 달라집니다. 나는

인터넷 :이 두 가지 중 어느 것도 원하는 형태로 사용하지 마십시오. (이 주제 이상으로 SQL 성능에 영향을 미치는 훨씬 더 큰 요소가 솔직히 있습니다).

이게 도움이 되길 바란다. 옵티마이 저의 작동 방식에 대한 책 장을 작성했지만 여기에 게시하는 것이 적절한 지 모르겠습니다. 따라서 영국의 SQLBits에서 최적화 프로그램이 높은 수준으로 작동하는 방식에 대해 이야기 한 링크에 게시한다고 게시하면 원하는 경우 검색의 다른 주요 단계를 좀 더 자세히 볼 수 있습니다 그것에 대해 배울 수 있습니다. 비디오 링크는 다음과 같습니다. https://sqlbits.com/Sessions/Event6/inside_the_sql_server_query_optimizer


2
내 믿음은 1또한 같은 확장을 겪고 있다는 것입니다 . 나는 이것을 perf 테스트에 기초한다. stackoverflow.com/questions/1597442/… 또한 1컬럼 레벨 권한이 사용 중일 때 예기치 않게 실패를 사용하여 쿼리에 대한 응답의 예를 참조하십시오.
Martin Smith

21

SQL-92 Standard에서 COUNT(*)구체적으로 "테이블 표현식의 카디널리티"(기본 테이블,`VIEW, 파생 테이블, CTE 등일 수 있음)를 의미합니다.

아이디어가 COUNT(*)파싱하기 쉽다 는 생각이 들었습니다. 다른 표현을 사용하는 것은 임의의 열을 참조하지 않도록 할 필요 파서 ( COUNT('a')여기서 a리터럴 및 COUNT(a)a이다 열 다른 결과를 얻을 수있다).

같은 맥락에서, COUNT(*)하나 이상의 벤더의 SQL 오퍼링을 작업 할 때 유용한 기술인 SQL 표준에 익숙한 인간 코더가 쉽게 선택할 수 있습니다.

또한 특별한 경우 SELECT COUNT(*) FROM MyPersistedTable; DBMS가 테이블의 카디널리티에 대한 통계를 보유 할 가능성이 있다고 생각합니다.

때문에 따라서, COUNT(1)그리고 COUNT(*)의미 동일, 내가 사용 COUNT(*).


1
DBA.SE에 대한 답변과 연결된 SQL-92 텍스트 : dba.stackexchange.com/questions/2511/…
gbn


12

옵티마이 저가 이상한 경우를 제외하고는 실질적인 차이가 없는지 확인해야합니다.

다른 것과 마찬가지로, 실제로 말할 수있는 유일한 방법은 특정 사례를 측정하는 것입니다.

즉, 나는 항상 사용했습니다 COUNT(*).


허용 된 답변에 따라, 이것은 MS SQL의 경우에는 사실이 아니며 실제로는 둘 사이에 차이가 없습니다.
David Manheim 2016 년

10

이 질문이 계속해서 나오면 여기에 또 하나의 대답이 있습니다. 초보자에게 "모범 사례"에 대해 궁금한 점을 추가하고 싶습니다.

SELECT COUNT(*) FROM something 쉬운 작업 인 레코드를 계산합니다.

SELECT COUNT(1) FROM something 레코드 당 1을 검색하고 null이 아닌 1을 계산합니다. 이는 기본적으로 레코드를 계산하는 것으로 더 복잡합니다.

이것을 말한 것 : 좋은 dbms는 두 번째 진술이 첫 번째 진술과 같은 수를 가져오고 불필요한 작업을하지 않기 위해 적절하게 해석한다는 것을 알았습니다. 따라서 일반적으로 두 명령문 모두 동일한 실행 계획을 생성하고 동일한 시간이 걸립니다.

그러나 가독성의 관점에서 첫 번째 진술을 사용해야합니다. 레코드 수를 계산하려고하므로식이 아닌 레코드 수를 계산하십시오. Null이 아닌 항목을 계산하려는 경우에만 COUNT (expression)를 사용하십시오.


8

8GB RAM hyper-v 상자에서 SQL Server 2012에 대한 빠른 테스트를 실행했습니다. 당신은 자신에 대한 결과를 볼 수 있습니다. 이 테스트를 실행하는 동안 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 (~ 1 억 7,800 만 행)

첫 번째 질문 :

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 엔진의 성능 차이가 없다고 쉽게 결론을 내릴 수 있습니다.

참고 : 두 실행에서 RAM 사용량이 거의 100 %에 달합니다. 두 실행을 모두 시작하기 전에 SQL Server 서비스를 다시 시작했습니다.


7
SET STATISTICS TIME ON

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

SQL Server 실행 시간 :
CPU 시간 = 31ms, 경과 시간 = 36ms

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

SQL Server 실행 시간 :
CPU 시간 = 46ms, 경과 시간 = 37ms

이 캐시를 수백 번 실행하여 매번 캐시를 지 웁니다. 서버로드가 다양 할 때마다 결과가 다르지만 거의 항상 count(*)CPU 시간이 더 깁니다.


14
이것을 재현 할 수 없습니다. SQL 2008 인스턴스에서 450 만 행의 테이블을 계산할 때에도 서로 몇 ms 내에 결과 count(*)count(1)반환합니다.
Jeff Atwood

2
때때로, 어떤 시스템에서는, 먼저 실행 된 명령문이 항상 더 빠르게 실행됩니다. 실행 순서를 무작위로 설정 했습니까?
JosephDoggie

@JosephDoggie는 이러한 측정 / 통계를 취하는 동안 모든 쿼리를 실행하기 전에 항상 SQL Server 서비스를 다시 시작해야합니다. SQL Server 서비스를 방금 시작하면 모든 실행이 완전히 독립적이되고 쿼리 순서는 중요하지 않습니다. 반면에 SQL Server 서비스를 다시 시작하지 않고 엔진이 일종의 실행 계획 캐싱을 수행하는 경우 나중에 실행되는 쿼리가 첫 번째 쿼리가 아닌 더 빠르게 실행됩니다.
RBT

실행 시간은 비교를 수행 할 때 정확한 쿼리 계획을 확인해야합니다. 서로 다른 경우 (예 : 해시 집계 대 정렬 + 스트림 집계) 결과는 비교할 수 없습니다. 따라서 더 많은 데이터없이 결론을 도출 할 것을 촉구합니다.
Conor Cunningham MSFT 22

3

기사 보여주는 COUNT(1)오라클은 단지 별칭은COUNT(*) 으로 증명 이 약은.

나는 몇몇 부분을 인용 할 것이다 :

“최적화 기”라고하는 데이터베이스 소프트웨어의 일부가 있으며 공식 문서에는“SQL 문을 실행하는 가장 효율적인 방법을 결정하는 내장 데이터베이스 소프트웨어”로 정의되어 있습니다.

옵티마이 저의 구성 요소 중 하나를 "트랜스포머"라고하며, 원래 SQL 문을보다 효율적인 의미 론적으로 동등한 SQL 문으로 다시 작성하는 것이 유리한지 여부를 판별하는 역할을합니다.

COUNT (1)을 사용하여 쿼리를 작성할 때 옵티마이 저가 수행하는 작업을 보시겠습니까?

ALTER SESSION권한 이있는 사용자를 사용 하면을 넣고 tracefile_identifier옵티 마이저 추적을 활성화하고 다음 COUNT(1)과 같이 select를 실행할 수 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(*).

또 다른 중요한 의견 : 20 년 전에 Oracle 7.3 이전에 Oracle에서 COUNT(*)실제로 더 빨랐습니다 .

Oracle은 신화 설명을 자동 조정하는 것을 좋아하기 때문에 Count (1)은 7.3 이후 count (*)로 다시 작성되었습니다. Oracle7 이전의 Oracle에서는 DETERMINISTIC 및 NON-DETERMINISTIC이 존재하기 전에 각 행에 대해 (1)을 함수로 평가해야했습니다.

20 년 전에 count (*)가 더 빨랐습니다.

Sql Server와 같은 다른 데이터베이스의 경우 각 데이터베이스마다 개별적으로 조사해야합니다.

이 질문은 Sql Server에만 해당되지만 데이터베이스에 대한 언급없이 동일한 주제에 대한 SO의 다른 질문은 닫히고이 답변과 중복 된 것으로 표시되었습니다.


1

모든 RDBMS에서 계산의 두 가지 방법은 결과의 측면에서 동일합니다. 성능과 관련하여 SQL Server의 성능 차이는 관찰하지 못했지만 PostgreSQL 11과 같은 일부 RDBMS 는이 게시물에서 볼 수 있듯이 인수 표현식의 Null 허용 여부를 확인하기 위해 최적의 구현이 덜 있음을 지적 할 가치가 있습니다.COUNT(1) .

실행할 때 1M 행에 대해 10 %의 성능 차이를 발견했습니다.

-- Faster
SELECT COUNT(*) FROM t;

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

0

COUNT (1)은 COUNT (*)와 크게 다르지 않습니다. NULLable COLUMN COUNTING 문제와 관련하여 COUNT (*)와 COUNT (<some col>)의 차이점을 간단하게 시연 할 수 있습니다.

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
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.