【问题标题】:How to derive a column name in the return type from input parameters to the function?如何从函数的输入参数导出返回类型中的列名?
【发布时间】:2016-07-06 17:15:16
【问题描述】:

我使用 Postgres 9.5 构建了这个函数:

CREATE or REPLACE FUNCTION func_getratio_laglag(_numeratorLAG text, _n1 int, _denominatorLAG text, _n2 int, _table text)
    RETURNS TABLE (date_t timestamp without time zone, customer_code text, index text, ratio real) AS
$BODY$
BEGIN
 RETURN QUERY EXECUTE 
        'SELECT 
    date_t,
    customer_code,
    index,
        (LAG('||quote_ident(_numeratorLAG)||',' || quote_literal(_n1)||') OVER W / LAG('||quote_ident(_denominatorLAG)||','|| quote_literal(_n2)||') OVER W) '
         || ' FROM ' || quote_ident(_table) 
         || ' WINDOW W AS (PARTITION BY customer_code ORDER BY date_t asc);';
END;
$BODY$ LANGUAGE plpgsql;

该函数所做的只是让我能够从指定的表中选择 2 个不同的列,并根据不同的滞后窗口计算它们之间的比率。要执行上面的函数,我使用以下查询:

SELECT * FROM func_getratio_laglag('order_first',1,'order_last',0,'customers_hist');

这将返回一个带有列标签date_tcustomer_codeindexratio 的表。我一直在努力解决如何将比率输出为动态列标签。也就是说,我想让它取决于输入参数,例如如果我运行上面的选择查询,那么我想要列标签date_tcustomer_codeindexorder_first_1_order_last_0

我被卡住了,有什么建议或提示吗?

【问题讨论】:

  • 您的文字是 Postgres 9.5,我冒昧地对标签进行了相应的调整。
  • 感谢欧文的帮助

标签: postgresql plpgsql postgresql-9.5


【解决方案1】:

如何从函数的输入参数导出返回类型中的列名?

简短的回答:不可能。
SQL 对列数据类型和名称非常严格。这些必须在最迟在通话时间之前或通话时声明。没有例外。没有真正动态的列名。

我能想到 3 个中途解决方法:

1。列别名

按原样使用您的函数(或者更确切地说是我在下面建议的审核版本)并在函数调用中添加列别名:

SELECT * FROM func_getratio_laglag('order_first',1,'order_last',0,'customers_hist')
AS f(date_t, customer_code, index, order_first_1_order_last_0)

我会这样做的。

2。列定义列表

创建您的函数以返回匿名记录:

RETURNS SETOF record

那么你必须在每次调用时提供一个列定义列表:

SELECT * FROM func_getratio_laglag('order_first',1,'order_last',0,'customers_hist')
AS f(date_t timestamp, customer_code text, index text, order_first_1_order_last_0 real)

我会不会那样做。

3。使用已注册的行类型作为多态输入/输出类型。

如果您碰巧手头有行类型,这将非常有用。您可以通过创建一个临时表来动态注册一个行类型,但这似乎对您的用例来说太过分了

此答案最后一章的详细信息:

功能审计

使用format() 使构建查询字符串更加安全和简单。
Read the manual if you are not familiar with it

CREATE OR REPLACE FUNCTION func_getratio_laglag(
                           _numerator_lag   text, _n1 int
                         , _denominator_lag text, _n2 int
                         , _table regclass)
   RETURNS TABLE (date_t timestamp, customer_code text, index text, ratio real) AS
$func$
BEGIN
   RETURN QUERY EXECUTE format (
     'SELECT date_t, customer_code, index
           , (lag(%I, %s) OVER w / lag(%I, %s) OVER w)  -- data type must match
      FROM   %s 
      WINDOW w AS (PARTITION BY customer_code ORDER BY date_t)'
    , _numerator_lag, _n1, _denominator_lag, _n2, _table::text
   );
END
$func$ LANGUAGE plpgsql;

注意表名的数据类型regclass。这是我的个人(可选)建议。

另外:我还建议不要在 Postgres 中使用大小写混合的标识符。

【讨论】:

  • 感谢您提供的详细信息。重申一下,我寻求的解决方案没有解决方案,您建议的解决方法(特别是第 1 点)只是重命名列,如果我决定更改任何输入,仍需要手动完成。可能需要寻找替代解决方案,例如将其输入 python 数据框。
  • @RRoszko:是的。但是当您考虑它时:无论如何您都将列名作为函数参数提供。是什么让您无法同时提供派生列别名?
  • 我最初设想使用循环,这样我就可以将参数输入到我的滞后计算中,例如0, 1, 2 等等。说实话,我当时并没有深入考虑它,它更像是一种临时计算。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-12-10
  • 2019-06-07
  • 1970-01-01
  • 2019-02-17
  • 1970-01-01
  • 2019-07-16
  • 2019-02-25
相关资源
最近更新 更多