在 PostgreSQL 中使用动态列名

use*_*935 2 postgresql stored-procedures dynamic-sql plpgsql postgresql-8.3

我正在使用 PostgreSSQL 8.3。

我想执行这个查询:

select COALESCE(col_fr,col,'Unknown') from my_table where id = 'abc';
Run Code Online (Sandbox Code Playgroud)

故事中的扭曲是列名colum_fr应该动态生成。my_table对于不同的语言有不同的列。就像是:

id   col   col_ja    col_fr
Run Code Online (Sandbox Code Playgroud)

我在水晶报告中使用此查询,我可以在其中传递语言的字符串参数:

select COALESCE(col_||{?parameter},col,'Unknown') from my_table where id = 'abc';
Run Code Online (Sandbox Code Playgroud)

如果{?language}value 是,它将在内部转换为如下内容fr

select COALESCE(col_||'fr',col,'Unknown') from my_table where id = 'abc';
Run Code Online (Sandbox Code Playgroud)

这永远不会奏效。

我不想使用选择大小写,使其动态化。
作为替代解决方案,我还尝试创建一个存储过程:

CREATE OR REPLACE FUNCTION get_policy_name (
  id text,
  lang   text,
  def_value text
)
RETURNS SETOF record AS
$body$
DECLARE
   sql text;
BEGIN
    sql := 'SELECT   COALESCE(col_'||quote_ident(lang)||',col,'||quote_literal(def_value)||')FROM my_table WHERE id ='||quote_literal(id);

   RETURN QUERY EXECUTE sql
END;
$body$ LANGUAGE plpgsql;
Run Code Online (Sandbox Code Playgroud)

哪个应该返回单个记录。
它不工作。我错过了什么?PostgreSSQL 8.3 支持RETURN QUERY EXECUTE吗?

Erw*_*ter 6

RETURN QUERY EXECUTE是在 Postgres 8.4 中引入的。
您的版本太旧了,现在不受支持。升级到更新的版本。

此外,结果中的动态列名很难获得。SQL 的一个原则是它想知道返回类型 - 包括名称 - 预先。

返回没有列定义列表的匿名记录仅适用于单个记录。否则,您必须在调用中提供列定义列表。他关于 SO 的问题的详细信息:
使用 PL/pgSQL 在 PostgreSQL 中返回多个字段作为记录

使用多态类型解决这个问题的方法有限。高级内容:
重构 PL/pgSQL 函数以返回各种 SELECT 查询的输出

顺便说一句,你的函数在现代 PL/pgSQL 中看起来像这样:

CREATE OR REPLACE FUNCTION get_policy_name (
  _id        int,
  _lang      text,
  _def_value text
) RETURNS TABLE (col text) AS
$func$
BEGIN
   RETURN QUERY EXECUTE
   format('SELECT COALESCE(%I, col, $1)
           FROM my_table WHERE id = $2'
         , 'col_' || _lang)
   USING _def_value, _id;
END
$func$ LANGUAGE plpgsql;
Run Code Online (Sandbox Code Playgroud)

称呼:

SELECT col AS col_fr FROM get_policy_name (1, 'fr', 'foo');
Run Code Online (Sandbox Code Playgroud)

在这里,我只是在调用中使用列别名来实现您想要的。比动态列名容易得多......