2013-02-18 66 views
5

如果我有以下的玩具查詢Postgresql plpgsql/sql支持where子句中的短路嗎?

SELECT * 
FROM my_tables 
WHERE my_id in (
    SELECT my_other_id 
    FROM my_other_tables 
) AND some_slow_func(arg) BETWEEN 1 AND 2; 

將在WHERE子句短路的首要條件這將有一個複雜的運行時間的第二個條件?

我正在研究一些實際上是plpgsql中FOR LOOP部分的sql,並且我可以對my_other_tables中存在的所有記錄進行迭代,然後在FOR LOOP的範圍內使用some_slow_func( )。但我很好奇,如果SQL支持,或plpgsql支持短路。

一些研究: 我看着在Postgres的郵件列表,發現一般這個說法SQL不支持短路:

http://www.postgresql.org/message-id/[email protected]

但其中一個答覆說,爲了能夠通過強制執行子查詢。我不確定他在說什麼。我知道子選票是什麼,但我不知道如何執行命令?有人能爲我澄清這一點嗎?

+0

我不認爲短路是相關的; SQL應該是面向集合的,其結果不應該依賴於評估的順序。對這個*的一個限制可能是兩個子查詢的聯合,這兩個子查詢都帶有一個LIMIT,並在整個查詢中增加一個額外的LIMIT。但LIMIT仍然是邊界線...評估的副作用不應該在真正的關係型RSBMS中實現(可能除了LATERAL)。總之:評估順序隻影響性能,而不是結果的正確性,恕我直言。這就是爲什麼我們應該把評估順序留給規劃者。 – wildplasser 2013-02-18 22:14:17

回答

6

如文件所述,WHERE子句中的評估順序應該是不可預測的。

它與子查詢不同。對於當前版本,推動評估順序的最簡單和常用的技術是在CTE中編寫子查詢。爲了確保IN(...)首先計算,你的代碼可以寫成:

WITH subquery AS 
(select * from my_tables 
    WHERE my_id in (SELECT my_other_id FROM my_other_tables) 
) 
SELECT * FROM subquery 
    WHERE some_slow_func(arg) BETWEEN 1 AND 2; 

別的東西,你可以調整你的函數的成本來通知它的速度慢了優化。一個函數的默認成本100,它可以像一個語句變更:

ALTER FUNCTION funcname(argument types) cost N; 

其中N是估計每個呼叫的成本,在應比Planner Cost Constants任意單位表示。

+0

是的,使用WITH將是一個很好的快速限制設置的方法。不知道爲什麼我沒有想到這一點....我喜歡用WITH。另外,甚至沒有考慮改變計劃者的成本。我知道它,但我一直讓postgres照顧優化。我以前從來沒有用過它,但會更多地閱讀它。感謝您引起我的注意! – enigmasck 2013-02-19 13:17:35

+0

當然,您定義函數時還可以包含'COST N'。 – jpmc26 2014-11-04 23:15:16

0

根據the Postgresql docsthis answer by Tom Lane,執行WHERE約束的順序是不可靠的。

我認爲這裏最好的選擇可能是將WHERE子句的其他部分添加到函數的頂部並且「快速失敗」;即在你的函數中運行my_id in ( SELECT my_other_id FROM my_other_tables),如果它沒有通過,那麼在你進行深入處理之前就返回。這應該會讓你產生同樣的效果。

+0

感謝您的參考。我一整天都在看postgresql文檔,並沒有遇到這種解釋。感謝您的快速回復。 – enigmasck 2013-02-18 19:52:04

+0

沒問題。一個提示 - 當你在看這些論壇時,底部通常會有「回覆」和「回覆」部分。如果您在響應下關注鏈接,您可能會獲得更多信息。在這種情況下,我所做的只是按照湯姆萊恩在OP中鏈接的帖子上的回覆。 – 2013-02-18 19:54:59

2

我知道這是一個老問題,但最近碰到類似的問題,並發現在WHERE子句中使用CASE謂詞對我更好。在上面的答案中:

SELECT * 
    FROM my_tables 
WHERE CASE WHEN my_id in (SELECT my_other_id 
          FROM my_other_tables) 
      AND some_slow_func(arg) BETWEEN 1 AND 2 
      THEN 1 
      ELSE 0 
     END = 1; 

這使得SQL稍微多於DB不可知。當然,如果您在my_id上有一些索引,它可能不會使用索引,但根據您所處的上下文,這可能是一個不錯的選擇。