Postgresql

使自定義聚合函式更易於使用(接受更多輸入類型而不創建變體)

  • August 6, 2018

最近我在 postgres 中編寫了一個自定義聚合函式,它將使用不同的列為匹配max/min聚合的行返回一個特定的列。

雖然程式碼本身執行良好,但為我可能需要的每個可能的輸入組合創建自定義數據類型有點麻煩。

這是我使用的程式碼

CREATE TYPE agg_tuple_text AS
(
   exists boolean,
   value numeric,
   text text
);

--------------------------------------------------------------------------------

CREATE FUNCTION valued_min(old_tuple agg_tuple_text, new_value numeric, new_text text)
   RETURNS agg_tuple_text
   LANGUAGE plpgsql
AS $$
   BEGIN
       IF (old_tuple).exists = false THEN
           RETURN (true, new_value, new_text);
       ELSIF (old_tuple).value > new_value THEN
           RETURN (true, new_value, new_text);
       ELSE
           RETURN old_tuple;
       END IF;
   END;
$$;
--------------------------------------------------------------------------------

CREATE FUNCTION unpack_agg_tuple_text(value agg_tuple_text)
   RETURNS text
   LANGUAGE plpgsql
AS $$
BEGIN
   IF (value).exists = false THEN
       RETURN NULL;
   ELSE
       RETURN (value).text;
   END IF;
END
$$;

--------------------------------------------------------------------------------

CREATE AGGREGATE valued_min(numeric, text)
(
       INITCOND = '(false, 0, null)',
       STYPE = agg_tuple_text,
       SFUNC = valued_min,
       FINALFUNC = unpack_agg_tuple_text
);

--------------------------------------------------------------------------------

-- Example
SELECT min(value) as min_value, valued_min(value, name) as min_name, max..., avg... FROM kv;
-- Output:
-- min_value | min_name           | ...
-- ----------+--------------------+----
--     11.11 | this is the lowest | ...

編輯:我的目標是為 TSDB 繪製一個最小/最大/平均圖表,並顯示每個最小和最大條目的名稱。

有沒有辦法在不為每種可能的組合創建所有這些的情況下實現這一目標?(可能是 Java 等中存在的某種通用參數)

  • 值列類型

    • 各種日期/時間類型
    • 數值類型
    • 也許是文字
    • (任何可比較的類型)
  • 數據列類型

    • 任何類型

如果我只能將它用於數據值就足夠了,因為它不用於該程式碼內的任何計算。不幸的是,自定義數據類型中不允許使用 anyelement 類型。

我已經考慮過使用 json 類型作為輸入,但這感覺有些不對,因為它失去了類型資訊(尤其是日期/時間類型)。


我使用沒有擴展的 Postgres 10,但如果可以使用 postgres 1x 或使用特殊擴展,我願意嘗試。


我也考慮過加入這些值,但後來我遇到了性能問題和具有相同值的潛在重複/行。

在詳細介紹之前 - 你確定你不是在重新發明輪子嗎?這可能會歸結為最流行的話題best-n-per-group

您的查詢:

SELECT min(value) as value, valued_min(value, name) as name FROM kv;

可以用股票 Postgres 重寫為:

SELECT value, name
FROM   kv
ORDER  BY value
LIMIT  1;

它還可以在索引或僅索引掃描上使用簡單的 btree(value)索引(value, name)-更快

我很確定任何其他範例也可以通過內置功能解決。要獲得每組一行,您的查詢將是:

SELECT grp_col, min(value) AS value, valued_min(value, name) AS name
FROM   kv
GROUP  BY grp_col;

用。。。來代替:

SELECT DISTINCT ON (grp_col)
      grp_col, value, name
FROM   kv
ORDER  BY grp_col, value;

再次,更快。而且用途廣泛得多。詳細解釋:

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