函数返回具有动态列名的行集

时间:2018-07-13 23:37:23

标签: postgresql

我有一个通用的键/值表,我想在函数中对其进行预处理以按键进行过滤,然后根据键来命名值。

该表是这样的,其中id指向另一个表(但是出于这个问题的目的,无关紧要):

CREATE TABLE keyed_values (id INTEGER, key TEXT, value TEXT, PRIMARY KEY(id, key));

INSERT INTO keyed_values(id, key, value) VALUES 
    (1, 'x', 'Value for x for 1'),
    (1, 'y', 'Value for y for 1'),
    (2, 'x', 'Value for x for 2'),
    (2, 'z', 'Value for z for 2');

等等。键值可以变化,并且并非所有ID都将使用相同的键。

具体地说,我想做的是创建一个函数,该函数将生成与给定键匹配的行。然后,它将为每个与key 命名为并带有键的匹配value返回一行。

因此,给定键名自变量'x'的函数的输出将与执行此命令的结果相同:

SELECT id, value x FROM keyed_values WHERE key = 'x';
 id |         x
----+-------------------
  1 | Value for x for 1
  2 | Value for x for 2
(2 rows)

这是我的刀路,将其他一些答案汇总在一起:

CREATE FUNCTION value_for(key TEXT) RETURNS SETOF RECORD AS $$
    BEGIN
        RETURN QUERY EXECUTE format('SELECT id, value %I FROM keyed_values WHERE key = %I', key, key);
    END;
$$ LANGUAGE 'plpgsql';

该功能被接受。但是当我执行它时,出现了这个错误:

SELECT * FROM value_for('x');
ERROR:  a column definition list is required for functions returning "record"
LINE 1: SELECT * FROM value_for('x');

我了解到它在告诉我什么,但没有解决方法。如何动态定义输出列名称?

1 个答案:

答案 0 :(得分:1)

key子句中使用的WHERE不是标识符,而是文字,因此该函数应类似于:

CREATE FUNCTION value_for(key TEXT) RETURNS SETOF RECORD AS $$
    BEGIN
        RETURN QUERY EXECUTE format('SELECT id, value %I FROM keyed_values WHERE key = %L', key, key);
    END;
$$ LANGUAGE plpgsql;

来自the documentation:

  

如果已将函数定义为返回记录数据类型,则必须存在一个别名或关键字AS,然后是形式为column_name data_type [,...]的列定义列表。列定义列表必须匹配该函数返回的实际列数和类型。

使用列定义列表:

SELECT * FROM value_for('x') AS (id int, x text);

请注意,您不需要动态SQL或plpgsql,因为列名是在上面的列表中定义的。简单的SQL函数应该更快一些:

CREATE OR REPLACE FUNCTION value_for(_key text)
RETURNS SETOF RECORD AS $$
    SELECT id, value
    FROM keyed_values
    WHERE key = _key
$$ LANGUAGE SQL;

如果您不喜欢被迫向每个函数调用添加列定义列表,则可以从函数返回表:

CREATE OR REPLACE FUNCTION value_for_2(_key text)
RETURNS TABLE(id int, x text) AS $$
    SELECT id, value
    FROM keyed_values
    WHERE key = _key
$$ LANGUAGE SQL;

SELECT * FROM value_for_2('x');

 id |         x         
----+-------------------
  1 | Value for x for 1
  2 | Value for x for 2
(2 rows)    

但是,这不能解决问题。无法动态定义从函数返回的列名。在执行查询之前,必须指定返回列的名称和类型。