對於絕對性能,SUM 更快還是 COUNT 更快?
這與計算符合特定條件的記錄數有關,例如
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 差異的具體事實?
你已經自己回答了這個問題。我有幾點要補充:
在PostgreSQL(和其他支持該
boolean
類型的 RDBMS)中,您可以boolean
直接使用測試結果。將其轉換為integer
andSUM()
:SUM((amount > 100)::int))
NULLIF()
或者在表達式中使用它andCOUNT()
:COUNT(NULLIF(amount > 100, FALSE))
或者用一個簡單的
OR NULL
:COUNT(amount > 100 OR NULL)
或其他各種表達方式。性能幾乎相同。
COUNT()
通常比 快得多SUM()
。不像SUM()
和像保羅已經評論過的,COUNT()
永遠不會返回NULL
,這可能很方便。有關的:從Postgres 9.4 開始,還有聚合
FILTER
子句。看:它比上述所有方法快約 5 - 10 %:
COUNT(*) FILTER (WHERE amount > 100)
如果查詢和你的測試案例一樣簡單,只有一個計數,沒有別的,你可以重寫:
SELECT count(*) FROM tbl WHERE amount > 100;
…這是真正的性能之王,即使沒有索引。
使用適用的索引,速度可以提高幾個數量級,尤其是使用僅索引掃描時。
基準
Postgres 13
db<>在這裡擺弄
與下面的 Postgres 10 的結果基本相同。(我還在小提琴中添加了一個沒有新並行性的測試,以比較蘋果和蘋果。)
Postgres 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<>在這裡擺弄
每組中的最後一個測試使用僅索引掃描,這就是為什麼它有助於計算所有行的三分之一。當涉及大約 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
性能上幾乎沒有任何真正的差異。