【问题标题】:Dynamically add a column with multiple values to any table using a PL/pgSQL function使用 PL/pgSQL 函数将具有多个值的列动态添加到任何表
【发布时间】:2018-07-04 22:13:54
【问题描述】:

我想使用函数/过程向“模板”表中添加具有多个值的附加列(例如期间名称),并对行进行笛卡尔积,因此我的“模板”与为新列提供了不同的值。

例如在我的template_country_channel 表中添加一个包含 2 个值的期间列:

SELECT * 
FROM   unnest(ARRAY['P1', 'P2']) AS prd(period)
     , template_country_channel 
ORDER BY  period DESC
        , sort_cnty
        , sort_chan;

/* 
-- this is equivalent to: 
(
  SELECT   'P2'::text AS period
         , *
  FROM template_country_channel
) UNION ALL ( 
  SELECT   'P1'::text AS period
         , *
  FROM template_country_channel
)
--
*/

此查询运行良好,但我想知道是否可以将其转换为 PL/pgSQL 函数/过程,提供要添加的新列值、要添加额外列的列(并可选择指定顺序按条件)。

我想做的是:

SELECT * 
FROM template_with_periods(
      'template_country_channel'           -- table name
    , ARRAY['P1', 'P2']                    -- values for the new column to be added
    , 'period DESC, sort_cnty, sort_chan'  -- ORDER BY string (optional)
);

并且与第一个查询具有相同的结果。

所以我创建了一个类似的函数:

CREATE OR REPLACE FUNCTION template_with_periods(template regclass, periods text[], order_by text) 
    RETURNS SETOF RECORD 
AS $BODY$

BEGIN
    RETURN QUERY EXECUTE 'SELECT * FROM unnest($2) AS prd(period), $1 ORDER BY $3' USING template, periods, order_by ;


END;
$BODY$
LANGUAGE 'plpgsql'
;

但是当我跑步时:

SELECT * 
FROM template_with_periods('template_country_channel', ARRAY['P1', 'P2'], 'period DESC, sort_cnty, sort_chan');

我有错误ERROR: 42601: a column definition list is required for functions returning “record”

经过一番谷歌搜索,似乎我需要定义列和类型的列表来执行RETURN QUERY(正如错误消息所准确指出的那样)。 不幸的是,整个想法是将该函数与许多“模板”表一起使用,因此列名和类型列表不固定。

  • 还有其他方法可以尝试吗?
  • 或者是让它工作的唯一方法是在函数内有一种方法来获取template表的列名和类型列表?

【问题讨论】:

  • 整个想法是将该函数与许多“模板”表一起使用,因此列名和类型列表不固定——这不是问题:@987654329 @您需要在调用方 f.ex 上登记列名和类型。 select * from template_with_periods(...) as t(period text, sort_cnty int, sort_chan text)
  • 实际上这意味着我必须在编写查询时指定列名/类型,并且我必须知道每个表结构——非常乏味,而这正是我想要避免的(如果 pgsql 允许)。
  • 不,很遗憾,它不允许这样做。您需要定义输出的 exact 结构。在函数定义中,或者在调用方(使用RETURNS SETOF RECORD)。

标签: postgresql plpgsql


【解决方案1】:

如果您希望输出列列表完全动态,我使用 refcursor 执行此操作:

CREATE OR REPLACE FUNCTION is_record_exists(tablename character varying, columns character varying[], keepcolumns character varying[] DEFAULT NULL::character varying[])
    RETURNS SETOF refcursor AS
$BODY$

DECLARE 
    ref refcursor;
    keepColumnsList text;
    columnsList text; 
    valuesList text;
    existQuery text;
    keepQuery text;
BEGIN
    IF keepcolumns IS NOT NULL AND array_length(keepColumns, 1) > 0 THEN
        keepColumnsList :=  array_to_string(keepColumns, ', ');
    ELSE
        keepColumnsList :=  'COUNT(*)';
    END IF;

    columnsList := (SELECT array_to_string(array_agg(name || ' = ' || value), ' OR ') FROM
        (SELECT unnest(columns[1:1]) AS name, unnest(columns[2:2]) AS value) pair);

    existQuery := 'SELECT ' || keepColumnsList || ' FROM ' || tableName || ' WHERE ' || columnsList;
    RAISE NOTICE 'Exist query: %', existQuery;

    OPEN ref FOR EXECUTE
        existQuery;
    RETURN next ref;
END;$BODY$
  LANGUAGE plpgsql;

然后需要调用FETCH ALL IN 来获取结果。详细语法here 或那里:https://stackoverflow.com/a/12483222/630169。看来这是目前唯一的办法了。希望通过 PROCEDURES 在 PostgreSQL 11 中有所改变。

【讨论】:

    猜你喜欢
    • 2018-02-10
    • 2014-07-18
    • 2013-08-07
    • 2018-02-10
    • 1970-01-01
    • 1970-01-01
    • 2016-05-07
    • 1970-01-01
    • 2021-01-21
    相关资源
    最近更新 更多