Postgresql

FROM 子句中的相關函式是否針對每一行執行?

  • July 10, 2022

我有一個很重的功能,我們稱之為fcalc(x,y) -> my_z. 我需要結果my_z既是過濾器(太低並且該行被丟棄)又是結果集中(以便我的客戶可以看到它)。我這樣寫查詢:

SELECT *, my_z
FROM big_table t, (SELECT * FROM fcalc(t.x, t.y)) as my_z
WHERE condition1 AND condition2 AND ... AND my_z > $threshold

我的問題是:所有其他條件是否會首先應用,在應用之前應該過濾掉非常多的行fcalc?我對數據庫很陌生。

不,Postgres 通常不會LATERAL為所有行評估子查詢中的函式。

它將big_table首先應用簡單的過濾器並僅對仍在競爭中的行執行該函式。

修復問題

您的查詢在語法上無效。

假設fcalc()返回一個值,這將起作用

SELECT * , my_z 
FROM big_table t, **LATERAL** (SELECT * FROM fcalc(tx, ty)) AS **f(my_z)** 
WHERE $condition1 AND $condition2 AND ... AND **f.my_z > $threshold**

並且應該解開只是:

SELECT *
FROM   big_table t
JOIN   LATERAL fcalc(t.x, t.y) AS f(my_z) ON f.my_z > $threshold
WHERE  $condition1
AND    $condition2
AND ... 

f.my_z > $thresholdfromWHERE子句移動到連接條件使查詢更易於閱讀,並且對查詢計劃沒有任何影響(使用 時[INNER] JOIN)。這會產生完全相同的查詢計劃:

SELECT *
FROM   big_table t, fcalc(t.x, t.y) f(my_z)
WHERE  $condition1
AND    f.my_z > $threshold
AND    $condition2
AND ... 

查詢計劃

在執行和過濾結果之前big_table,任何一個固定查詢都將首先應用過濾行的謂詞。fcalc()

您可以使用EXPLAIN ANALYZE. 假設您big_table有 8 行,其中 5 行未通過您的$conditionN過濾器,其餘 3 行中的 1 行未通過f.my_z > $threshold。你會看到類似的東西:

嵌套循環(成本=0.00..1.17 行=3 寬度=79)(實際時間=0.026..0.027 行=1 循環=1)
-> big_table t 上的 Seq Scan(成本=0.00..1.10 行=3 寬度=75)(實際時間=0.007..0.009 行=3 循環=1)
過濾器:(id > 5)
       **過濾器刪除的行數:5**
-> 對 fcalc f 進行函式掃描(成本=0.00..0.02 行=1 寬度=4)
(實際時間=0.005..0.005 行=0**循環=3**)
過濾器:(my_z > 9)
       **過濾器刪除的行數:1**
規劃時間:0.101 ms
執行時間:0.043 毫秒

意思fcalc()是,在範例中只執行了 3 次。實際上,您應該看到大表的索引掃描,但都是一樣的。

如果在執行查詢之前將 GUC 設置track_functions為,則可以進一步驗證這一點。手冊:pl``EXPLAIN ANALYZE

啟用對函式呼叫計數和使用時間的跟踪。指定pl 僅跟踪過程語言函式,all同時跟踪 SQL 和 C 語言函式。預設值為none,禁用函式統計跟踪。只有超級使用者可以更改此設置。

筆記

無論此設置如何,都不會跟踪簡單到可以“內聯”到呼叫查詢中的 SQL 語言函式。

然後檢查在執行查詢之前和之後實際呼叫函式的頻率:

SELECT calls
FROM   pg_catalog.pg_stat_user_functions
WHERE  funcid = 'fcalc'::regproc

有關演員'fcalc'::regproc表的詳細資訊,請參閱手冊。

在旁邊

Postgres 還將根據它們的估計成本對同一級別的過濾器進行優先級排序。您可以使用我上面列出的工具進行驗證。修補COST簡單 plpgsql 函式的設置…

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