이는 특정 조건과 일치하는 레코드 수를 계산하는 것과 관련이 있습니다 (예 : invoice amount > $100
있습니다.
나는 선호하는 경향이있다
COUNT(CASE WHEN invoice_amount > 100 THEN 1 END)
그러나 이것은 유효합니다
SUM(CASE WHEN invoice_amount > 100 THEN 1 ELSE 0 END)
COUNT가 두 가지 이유로 선호된다고 생각했을 것입니다.
- 의도를 전달합니다.
COUNT
COUNT
아마 간단한 포함i += 1
SUM 간단한 정수 값으로 발현에 포함되지 수있는 반면, 조작 어딘가에.
누구든지 특정 RDBMS의 차이점에 대한 특정 사실이 있습니까?
답변
당신은 대부분 스스로 질문에 스스로 대답했습니다. 추가해야 할 몇 가지 morsels가 있습니다.
에서 PostgreSQL을 (그리고 지원하는 다른 RDBMS boolean
유형) 당신은 사용할 수 있습니다 boolean
직접 테스트의 결과입니다. 그것을 캐스팅 integer
과 SUM()
:
SUM((amount > 100)::int))
또는 NULLIF()
표현식에 사용하십시오 COUNT()
.
COUNT(NULLIF(amount > 100, FALSE))
또는 간단한 OR NULL
:
COUNT(amount > 100 OR NULL)
또는 다양한 다른 표현들. 성능은 거의 동일합니다 . COUNT()
일반적으로보다 약간 빠릅니다 SUM()
. 달리 SUM()
와 같은 폴 이미 주석 , COUNT()
결코 돌아 NULL
편리 할 수 있습니다. 관련 :
Postgres 9.4FILTER
부터는 절도 있습니다. 세부:
그것은의 빠른 위의 모든보다 약 5로 – 10 % :
COUNT(*) FILTER (WHERE amount > 100)
경우 쿼리가 테스트 케이스로 간단하다, 단 하나의 수와 다른 아무것도, 당신은 다시 작성할 수 있습니다 :
SELECT count(*) FROM tbl WHERE amount > 100;
인덱스가 없어도 진정한 성능의 왕입니다.
적용 가능한 색인을 사용하면 특히 색인 전용 스캔의 경우 수십 배 더 빠를 수 있습니다.
벤치 마크
포스트그레스 10
골재를 포함하여 Postgres 10에 대한 새로운 일련의 테스트를 실행했습니다. FILTER
조항과 소규모 및 대규모 카운트의 역할이 포함됩니다.
간단한 설정 :
CREATE TABLE tbl (
tbl_id int
, amount int NOT NULL
);
INSERT INTO tbl
SELECT g, (random() * 150)::int
FROM generate_series (1, 1000000) g;
-- only relevant for the last test
CREATE INDEX ON tbl (amount);
실제 시간은 배경 소음과 테스트 베드의 특성으로 인해 약간 다릅니다. 더 큰 테스트 세트에서 일반적인 최상의 시간을 표시 합니다. 이 두 경우는 본질을 포착해야합니다.
테스트 1 계산 ~ 모든 행의 1 %
SELECT COUNT(NULLIF(amount > 148, FALSE)) FROM tbl; -- 140 ms
SELECT SUM((amount > 148)::int) FROM tbl; -- 136 ms
SELECT SUM(CASE WHEN amount > 148 THEN 1 ELSE 0 END) FROM tbl; -- 133 ms
SELECT COUNT(CASE WHEN amount > 148 THEN 1 END) FROM tbl; -- 130 ms
SELECT COUNT((amount > 148) OR NULL) FROM tbl; -- 130 ms
SELECT COUNT(*) FILTER (WHERE amount > 148) FROM tbl; -- 118 ms -- !
SELECT count(*) FROM tbl WHERE amount > 148; -- without index -- 75 ms -- !!
SELECT count(*) FROM tbl WHERE amount > 148; -- with index -- 1.4 ms -- !!!
db <> 바이올린 여기
테스트 2 계수 ~ 모든 행의 33 %
SELECT COUNT(NULLIF(amount > 100, FALSE)) FROM tbl; -- 140 ms
SELECT SUM((amount > 100)::int) FROM tbl; -- 138 ms
SELECT SUM(CASE WHEN amount > 100 THEN 1 ELSE 0 END) FROM tbl; -- 139 ms
SELECT COUNT(CASE WHEN amount > 100 THEN 1 END) FROM tbl; -- 138 ms
SELECT COUNT(amount > 100 OR NULL) FROM tbl; -- 137 ms
SELECT COUNT(*) FILTER (WHERE amount > 100) FROM tbl; -- 132 ms -- !
SELECT count(*) FROM tbl WHERE amount > 100; -- without index -- 102 ms -- !!
SELECT count(*) FROM tbl WHERE amount > 100; -- with index -- 55 ms -- !!!
db <> 바이올린 여기
각 세트의 마지막 테스트는 인덱스 전용 스캔을 사용 했기 때문에 모든 행의 1/3을 계산하는 데 도움이되었습니다. 일반 인덱스 또는 비트 맵 인덱스 스캔은 모든 행의 약 5 % 이상을 포함하는 경우 순차적 스캔과 경쟁 할 수 없습니다.
Postgres 9.1의 오래된 테스트
EXPLAIN ANALYZE
PostgreSQL 9.1.6의 실제 테이블에서 빠른 테스트를 실행했는지 확인했습니다 .
조건으로 규정 된 184568 개의 행 중 74208 개 kat_id > 50
. 모든 쿼리는 동일한 결과를 반환합니다. 캐싱 효과를 제외하고 각각 10 번씩 차례로 실행하여 최상의 결과를 메모로 추가했습니다.
SELECT SUM((kat_id > 50)::int) FROM log_kat; -- 438 ms
SELECT COUNT(NULLIF(kat_id > 50, FALSE)) FROM log_kat; -- 437 ms
SELECT COUNT(CASE WHEN kat_id > 50 THEN 1 END) FROM log_kat; -- 437 ms
SELECT COUNT((kat_id > 50) OR NULL) FROM log_kat; -- 436 ms
SELECT SUM(CASE WHEN kat_id > 50 THEN 1 ELSE 0 END) FROM log_kat; -- 432 ms
실제 성능 차이는 거의 없습니다.
답변
이것은 SQL Server 2012 RTM에 대한 테스트입니다.
if object_id('tempdb..#temp1') is not null drop table #temp1;
if object_id('tempdb..#timer') is not null drop table #timer;
if object_id('tempdb..#bigtimer') is not null drop table #bigtimer;
GO
select a.*
into #temp1
from master..spt_values a
join master..spt_values b on b.type='p' and b.number < 1000;
alter table #temp1 add id int identity(10,20) primary key clustered;
create table #timer (
id int identity primary key,
which bit not null,
started datetime2 not null,
completed datetime2 not null,
);
create table #bigtimer (
id int identity primary key,
which bit not null,
started datetime2 not null,
completed datetime2 not null,
);
GO
--set ansi_warnings on;
set nocount on;
dbcc dropcleanbuffers with NO_INFOMSGS;
dbcc freeproccache with NO_INFOMSGS;
declare @bigstart datetime2;
declare @start datetime2, @dump bigint, @counter int;
set @bigstart = sysdatetime();
set @counter = 1;
while @counter <= 100
begin
set @start = sysdatetime();
select @dump = count(case when number < 100 then 1 end) from #temp1;
insert #timer values (0, @start, sysdatetime());
set @counter += 1;
end;
insert #bigtimer values (0, @bigstart, sysdatetime());
set nocount off;
GO
set nocount on;
dbcc dropcleanbuffers with NO_INFOMSGS;
dbcc freeproccache with NO_INFOMSGS;
declare @bigstart datetime2;
declare @start datetime2, @dump bigint, @counter int;
set @bigstart = sysdatetime();
set @counter = 1;
while @counter <= 100
begin
set @start = sysdatetime();
select @dump = SUM(case when number < 100 then 1 else 0 end) from #temp1;
insert #timer values (1, @start, sysdatetime());
set @counter += 1;
end;
insert #bigtimer values (1, @bigstart, sysdatetime());
set nocount off;
GO
개별 런 및 배치를 개별적으로보고
select which, min(datediff(mcs, started, completed)), max(datediff(mcs, started, completed)),
avg(datediff(mcs, started, completed))
from #timer group by which
select which, min(datediff(mcs, started, completed)), max(datediff(mcs, started, completed)),
avg(datediff(mcs, started, completed))
from #bigtimer group by which
5 회 실행 (및 반복) 한 결과는 매우 결정적입니다.
which ** Individual
----- ----------- ----------- -----------
0 93600 187201 103927
1 93600 187201 103864
which ** Batch
----- ----------- ----------- -----------
0 10108817 10545619 10398978
1 10327219 10498818 10386498
SQL Server 타이머의 세분성으로 측정 할 때 실행 조건에 차이가있는 것보다 실행 조건에 훨씬 더 많은 변동이 있음을 보여줍니다. 어느 버전이든 맨 위에 올 수 있으며 내가 가진 최대 분산은 2.5 %입니다.
그러나 다른 접근 방식을 취하십시오.
set showplan_text on;
GO
select SUM(case when number < 100 then 1 else 0 end) from #temp1;
select count(case when number < 100 then 1 end) from #temp1;
StmtText (SUM)
|--Compute Scalar(DEFINE:([Expr1003]=CASE WHEN [Expr1011]=(0) THEN NULL ELSE [Expr1012] END))
|--Stream Aggregate(DEFINE:([Expr1011]=Count(*), [Expr1012]=SUM([Expr1004])))
|--Compute Scalar(DEFINE:([Expr1004]=CASE WHEN [tempdb].[dbo].[#temp1].[number]<(100) THEN (1) ELSE (0) END))
|--Clustered Index Scan(OBJECT:([tempdb].[dbo].[#temp1]))
StmtText (COUNT)
|--Compute Scalar(DEFINE:([Expr1003]=CONVERT_IMPLICIT(int,[Expr1008],0)))
|--Stream Aggregate(DEFINE:([Expr1008]=COUNT([Expr1004])))
|--Compute Scalar(DEFINE:([Expr1004]=CASE WHEN [tempdb].[dbo].[#temp1].[number]<(100) THEN (1) ELSE NULL END))
|--Clustered Index Scan(OBJECT:([tempdb].[dbo].[#temp1]))
내가 읽은 바에 따르면 SUM 버전이 약간 더 많은 것으로 보입니다. SUM 외에 COUNT 를 수행하고 있습니다. 말했듯이, COUNT(*)
다르며 COUNT([Expr1004])
(NULL 건너 뛰기, 더 많은 논리) 보다 빠릅니다 . 합리적인 최적화는 것을 알게 될 것이다 [Expr1004]
에서 SUM([Expr1004])
합계 버전은 “INT”유형에 그래서 정수 레지스터를 사용합니다.
어쨌든, 여전히 COUNT
대부분의 RDBMS에서 버전이 더 빠를 것이라고 생각하지만 테스트를 통해 얻은 결론 SUM(.. 1.. 0..)
은 앞으로 SQL Server에 대해 사용할 때 ANSI 경고가 발생하는 것 외에 다른 이유는 없습니다. COUNT
.
답변
내 경험에 따르면 약 10,000,000의 쿼리에서 두 가지 방법 모두 추적을 수행하면 Count (*)가 CPU의 약 두 배를 사용하고 조금 더 빠르게 실행됩니다. 하지만 내 쿼리에는 필터가 없습니다.
카운트(*)
CPU...........: 1828
Execution time: 470 ms
합 (1)
CPU...........: 3859
Execution time: 681 ms