[英]sum() vs. count()
考慮在PostgreSQL中實現的投票系統,其中每個用戶可以在“foo”上向上或向下投票。 有一個foo
表,用於存儲所有的“富信息”,以及votes
存儲表user_id
, foo_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.