Postgresql

當列的類型不同時,Postgres 中是否有任何方法可以參數化排序順序列的過程?

  • September 21, 2020

我正在嘗試編寫一個函式來執行(一些複雜的事情)並根據參數以不同的順序返回結果。

簡化版本如下所示:

CREATE OR REPLACE FUNCTION test(order_column text)
   RETURNS TABLE(thing1 bigint,thing2 text, thing3 timestamp without time zone)
   LANGUAGE 'plpgsql'

AS $BODY$
BEGIN
RETURN QUERY
   SELECT thing1, thing2::text, thing3 FROM some_table
   ORDER BY 
       CASE WHEN order_column='id' THEN thing1
       ELSE thing3
       END
   DESC;
   
END;
$BODY$;

不幸的是,thing1 是一個 bigint,而 thing3 是一個時間戳,當我嘗試執行該函式時,我收到一個錯誤,說 bigint 和時間戳類型無法匹配,我將其解釋為從案例返回的類型需要是相同(或至少兼容)。我不能將它們都轉換為文本,因為值的範圍沒有正確排序。

我嘗試返回列號而不是列名 - 這至少會執行,但它會忽略列順序(在函式中或僅作為簡單語句執行)。例如,

SELECT * FROM some_table ORDER BY 1;

工作正常但

SELECT * FROM some_table ORDER BY CASE WHEN TRUE THEN 1 ELSE 2 END;

按第 1 列排序

我的解決方法是

if column_order='first' then
 (masses of complex stuff)
 SELECT ... ORDER BY thing1
else
 (masses of complex stuff, duplicated)
 SELECT ... ORDER BY thing3
end if;

但這太可怕了,我真的希望有其他方法可以解決這個問題,而且我目前缺少一些東西。

有什麼辦法可以做我想做的事嗎?

小心條件排序,它可能會創建錯誤的查詢計劃,有時會強制進行表掃描。如果過濾和連接子句,或者只是實際數據的大小,意味著您最後有少量行要排序,那麼這不是問題,這樣的事情會起作用:

ORDER BY CASE WHEN ordering_column = 'id'        THEN id        ELSE NULL END
      , CASE WHEN ordering_column = 'timestamp' THEN timestamp ELSE NULL END

事實上它無論如何都會起作用,它可能只是對於大量數據來說效率低下。

對於較大的輸出,您的解決方法可能更有效,因為它可以更好地利用索引進行排序。另一種選擇是有兩個過程,一個用於每個排序,或者根據需要呼叫每個過程,或者讓您的主過程呼叫其他過程,具體取決於它在參數中傳遞的排序順序。根據 postgres 如何處理過程的記憶體查詢計劃,這可能$$ † $$避免將一個案例的記憶體計劃用於另一個效率低得多的案例。

$$ † $$我根本不是 pg 內部的專家,但是由於這種原因,單個“廚房水槽”過程和具有條件排序等的查詢可能會成為 SQL Server 中的性能殺手。

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