Sql-Server

對於絕對性能,SUM 更快還是 COUNT 更快?

  • November 26, 2021

這與計算符合特定條件的記錄數有關,例如invoice amount > $100

我傾向於更喜歡

COUNT(CASE WHEN invoice_amount > 100 THEN 1 END)

但是,這同樣有效

SUM(CASE WHEN invoice_amount > 100 THEN 1 ELSE 0 END)

我會認為 COUNT 更可取,原因有兩個:

  1. 傳達意圖,即COUNT
  2. COUNT 可能在某處涉及一個簡單的i += 1操作,而 SUM 不能指望它的表達式是一個簡單的整數值。

有沒有人有關於特定 RDBMS 差異的具體事實?

你已經自己回答了這個問題。我有幾點要補充:

PostgreSQL(和其他支持該boolean類型的 RDBMS)中,您可以boolean直接使用測試結果。將其轉換為integerand SUM()

SUM((amount > 100)::int))

NULLIF()或者在表達式中使用它and COUNT()

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 &gt; 148, FALSE))            FROM tbl; -- 140 ms
SELECT SUM((amount &gt; 148)::int)                      FROM tbl; -- 136 ms
SELECT SUM(CASE WHEN amount &gt; 148 THEN 1 ELSE 0 END) FROM tbl; -- 133 ms
SELECT COUNT(CASE WHEN amount &gt; 148 THEN 1 END)      FROM tbl; -- 130 ms
SELECT COUNT((amount &gt; 148) OR NULL)                 FROM tbl; -- 130 ms
SELECT COUNT(*) FILTER (WHERE amount &gt; 148)          FROM tbl; -- 118 ms -- !

SELECT count(*) FROM tbl WHERE amount &gt; 148; -- without index  --  75 ms -- !!
SELECT count(*) FROM tbl WHERE amount &gt; 148; -- with index     --   1.4 ms -- !!!

db<>在這裡擺弄

測試 2計數〜所有行的 33%

SELECT COUNT(NULLIF(amount &gt; 100, FALSE))            FROM tbl; -- 140 ms
SELECT SUM((amount &gt; 100)::int)                      FROM tbl; -- 138 ms
SELECT SUM(CASE WHEN amount &gt; 100 THEN 1 ELSE 0 END) FROM tbl; -- 139 ms
SELECT COUNT(CASE WHEN amount &gt; 100 THEN 1 END)      FROM tbl; -- 138 ms
SELECT COUNT(amount &gt; 100 OR NULL)                   FROM tbl; -- 137 ms
SELECT COUNT(*) FILTER (WHERE amount &gt; 100)          FROM tbl; -- 132 ms -- !

SELECT count(*) FROM tbl WHERE amount &gt; 100; -- without index  -- 102 ms -- !!
SELECT count(*) FROM tbl WHERE amount &gt; 100; -- with index     --  55 ms -- !!!

db<>在這裡擺弄

每組中的最後一個測試使用僅索引掃描,這就是為什麼它有助於計算所有行的三分之一。當涉及大約 5% 或更多的行時,普通索引或點陣圖索引掃描無法與順序掃描競爭。

Postgres 9.1 的舊測試

為了驗證我EXPLAIN ANALYZE對 PostgreSQL 9.1.6 中的真實表進行了快速測試。

184568 行中的 74208 行符合條件kat_id &gt; 50。所有查詢都返回相同的結果。我依次執行了 10 次以排除記憶體效果,並將最佳結果作為註釋附加:

SELECT SUM((kat_id &gt; 50)::int)                      FROM log_kat; -- 438 ms
SELECT COUNT(NULLIF(kat_id &gt; 50, FALSE))            FROM log_kat; -- 437 ms
SELECT COUNT(CASE WHEN kat_id &gt; 50 THEN 1 END)      FROM log_kat; -- 437 ms
SELECT COUNT((kat_id &gt; 50) OR NULL)                 FROM log_kat; -- 436 ms
SELECT SUM(CASE WHEN kat_id &gt; 50 THEN 1 ELSE 0 END) FROM log_kat; -- 432 ms

性能上幾乎沒有任何真正的差異。

引用自:https://dba.stackexchange.com/questions/27558