簡體   English   中英

sum()與count()

[英]sum() vs. count()

考慮在PostgreSQL中實現的投票系統,其中每個用戶可以在“foo”上向上或向下投票。 有一個foo表,用於存儲所有的“富信息”,以及votes存儲表user_idfoo_id ,並vote ,其中vote是+1或-1。

要獲得每個foo的投票結果,以下查詢將起作用:

SELECT sum(vote) FROM votes WHERE foo.foo_id = votes.foo_id;

但是,以下內容也可以正常工作:

(SELECT count(vote) FROM votes 
 WHERE foo.foo_id = votes.foo_id 
 AND votes.vote = 1)
- (SELECT count(vote) FROM votes 
   WHERE foo.foo_id = votes.foo_id 
   AND votes.vote = (-1))

我目前在votes.foo_id上有一個索引。

哪種方法更有效? (換句話說,哪個會運行得更快?)我對PostgreSQL特定的答案和一般的SQL答案感興趣。

編輯

很多答案都考慮到vote為空的情況。 我忘了提到投票列上有一個NOT NULL約束。

此外,許多人指出,第一個更容易閱讀。 是的,這絕對是真的,如果一位同事寫了第二篇,我會憤怒地爆發,除非有表演的必要性。 從來沒有,問題仍然在於兩者的表現。 (從技術上來說,如果第一個查詢方法要慢,它不會是這種罪行寫入第二個查詢。)

當然,第一個例子更快,更簡單,更容易閱讀。 甚至在被水生生物拍打之前應該是顯而易見的。 雖然sum()count()略貴,但更重要的是,第二個例子需要兩次掃描。

但是也有一個實際的區別sum()可以返回NULL ,而count()則不會。 我引用了關於聚合函數手冊

應該注意,除了count之外,這些函數在沒有選擇行時返回空值。 特別是,沒有行的總和返回null,而不是像人們預期的那樣為零,

由於您似乎在性能優化方面存在弱點,因此這里有一個您可能會喜歡的細節: count(*)略快於count(vote) 如果vote為NOT NULL則僅等效。 使用EXPLAIN ANALYZE測試性能。

仔細檢查

這兩個查詢都是語法上的廢話,獨自站立。 只有從較大查詢的SELECT列表中復制它們才有意義:

SELECT *, (SELECT sum(vote) FROM votes WHERE votes.foo_id = foo.foo_id)
FROM   foo;

這里重要的一點是相關子查詢 - 如果您只在查詢中閱讀一小部分 votes ,這可能沒問題。 我們會看到其他WHERE條件,您應該有匹配的索引。

在Postgres 9.3或更高版本中,替代的,更清潔,100%等效的解決方案將使用LEFT JOIN LATERAL ... ON true

SELECT *
FROM   foo f
LEFT   JOIN LATERAL (
   SELECT sum(vote) FROM votes WHERE foo_id = f.foo_id
   ) v ON true;

通常類似的表現。 細節:

但是 ,在從表格votes讀取大部分或全部內容時 ,這將(更快)更快:

SELECT f.*, v.score
FROM   foo f
JOIN   (
   SELECT foo_id, sum(vote) AS score
   FROM   votes
   GROUP  BY 1
   ) v USING (foo_id);

首先在子查詢中聚合值,然后加入到結果中。
關於USING

第一個會更快。 您可以通過簡單的方式嘗試。

生成一些數據:

CREATE TABLE votes(foo_id integer, vote integer);
-- Insert 1000000 rows into 100 foos (1 to 100)
INSERT INTO votes SELECT round(random()*99)+1, CASE round(random()) WHEN 0 THEN -1 ELSE 1 END FROM generate_series(1, 1000000);
CREATE INDEX idx_votes_id ON votes (foo_id);

檢查兩個

EXPLAIN ANALYZE SELECT SUM(vote) FROM votes WHERE foo_id = 5;
EXPLAIN ANALYZE SELECT (SELECT COUNT(*) AS count FROM votes WHERE foo_id=5 AND vote=1) - (SELECT COUNT(*)*-1 AS count FROM votes WHERE foo_id=5 AND vote=-1);

但事實是,它們並不等同,為了確保第一個作為第二個,你需要對待null案例:

SELECT COALESCE(SUM(vote), 0) FROM votes WHERE foo_id = 5;

還有一件事。 如果您使用的是PostgreSQL 9.2,則可以使用其中的兩列創建索引,這樣您就有可能使用僅索引掃描:

CREATE INDEX idx_votes_id ON votes (foo_id, vote);

但! 在某些情況下,這個索引可能是最差的,所以你應該嘗試使用兩個並運行EXPLAIN ANALYZE以查看哪個是最好的,或者甚至創建兩個並檢查哪個PostgreSQL使用最多(並排除另一個)。

我希望第一個查詢能夠更快地工作,因為這是一個單一的查詢,並且它更具可讀性(如果你不得不在一段時間之后再回到這個問題,那就很方便了)。

第二個查詢包含兩個查詢。 您只能獲得一個結果,就像它是一個查詢一樣。

也就是說,為了絕對確定哪些更適合你,我會用兩個表填充大量的偽數據並檢查查詢執行時間。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM