使用 GROUP BY 和 HAVING 時如何避免兩次呼叫函式?
我有一個帶有父子關係表的 PostgreSQL 數據庫(9.2)。我有一個查詢來查找具有多個父節點的節點。
以下查詢有效並返回正確的結果:
SELECT node,parents FROM ( SELECT nr.child AS node, COUNT(nr.parent) AS parents FROM node_relation nr GROUP BY nr.child ) AS count WHERE parents > 1;
結果集:
node | parents --------+--------- n21174 | 2 n8635 | 2 (2 rows)
表定義為:
Table "public.node_relation" Column | Type | Modifiers -------------+-----------------------+--------------- child | character varying(50) | not null parent | character varying(50) | not null Indexes: "node_relation_pkey" PRIMARY KEY, btree (child, parent)
我重寫了查詢以不使用子選擇:
SELECT child AS node, COUNT(parent) AS parents FROM node_relation GROUP BY child HAVING COUNT(parent) > 1;
新查詢有效,但我想知道 COUNT 函式被多次呼叫。
**更新:**這是查詢計劃:
QUERY PLAN ------------------------------------------------------------------------------------------------------------- GroupAggregate (cost=0.00..1658.81 rows=19970 width=16) Filter: (count(parent) > 1) -> Index Only Scan using node_relation_pkey on node_relation (cost=0.00..1259.40 rows=19971 width=16)
我更喜歡使用
parents
別名,但以下不起作用:SELECT child AS node, COUNT(parent) AS parents FROM node_relation GROUP BY child HAVING parents > 1; ERROR: column "parents" does not exist LINE 1: ...parents FROM node_relation GROUP BY child HAVING parents > ... ^
PostgreSQL 會優化 ? 的多次呼叫
COUNT
?如果沒有,是否有這種查詢的替代形式會更有效?
您的第二個查詢(您使用
HAVING
子句實現它的那個)可能更快。在您的第一個查詢(使用子選擇)中,postgres 必須計算整個表的計數值。在您的第二個查詢中,一旦計數值超過 1,它就可以開始忽略要計數的行(儘管我不 100% 知道 postgres 是否足夠聰明地做到這一點 - 不過我很確定它是)。由於
COUNT()
是一個聚合函式,因此無論返回的行數如何,它都會執行它執行的次數。如果您有一個不是聚合函式的函式,那麼在子選擇中執行您的組和 where/have 子句可能會更快。我所指的範例:
SELECT some_non_agg_function(a.id, a.child) FROM join_tab1 a GROUP BY a.id, a.child HAVING COUNT(a.id) > 1; -- probably not as fast as WITH rows_to_process AS ( SELECT DISTINCT id, child FROM join_tab1 a GROUP BY a.id, a.child HAVING COUNT(a.id) > 1 ) SELECT some_non_agg_function(id, child) FROM rows_to_process;
HAVING
要具體回答您的問題 - 是的,postgres 將跟踪它計算的聚合值並在子句中重新使用它們(而不是重新計算它們) 。我相信它也會在SELECT
子句中重新使用它們(如果出於某種奇怪的原因,您在 中多次執行完全相同的聚合SELECT
)引用Postgres 的優秀文件(我的粗體)
了解聚合與 SQL 的 WHERE 和 HAVING 子句之間的互動非常重要。**WHERE 和 HAVING 之間的根本區別在於:WHERE 在計算組和聚合之前選擇輸入行(因此,它控制哪些行進入聚合計算),而 HAVING 在計算組和聚合之後選擇組行。**因此,WHERE 子句不能包含聚合函式;嘗試使用聚合來確定哪些行將作為聚合的輸入是沒有意義的。另一方面,HAVING 子句總是包含聚合函式。(嚴格來說,您可以編寫不使用聚合的 HAVING 子句,但它很少有用。在 WHERE 階段可以更有效地使用相同的條件。)
這並沒有具體說它重新使用計算值.. 但它暗示它說該
HAVING
子句是在計算聚合之後使用的。