plpgsql - 通过返回结果集自动检测列类型

时间:2013-07-04 08:05:22

标签: sql postgresql types plpgsql

我得到了Postgresql 8.4

我有一个用户表

user_id INT,
user_name VARCHAR(255),
user_email VARCHAR(255),
user_salt VARCHAR(255)

和2个功能:

一个SETOF

CREATE FUNCTION test ()
  RETURNS SETOF "user"
AS
  $BODY$
  BEGIN
    RETURN QUERY SELECT
      *
    FROM
      "user";
  END;
$BODY$
LANGUAGE 'plpgsql' VOLATILE;

一个TABLE

CREATE FUNCTION test ()
  RETURNS TABLE (id INT, name VARCHAR, email VARCHAR)
AS
  $BODY$
  BEGIN
    RETURN QUERY SELECT
      "user".user_id, "user".user_name, "user".user_email
    FROM
      "user";
  END;
$BODY$
LANGUAGE 'plpgsql' VOLATILE;

SETOF自动填充列类型,但我无法设置列名以及在结果中选择的列。到TABLE我可以切断user_前缀并设置确切的列名,但我必须手动设置列类型。

是否有可能获得两者的优势?

2 个答案:

答案 0 :(得分:2)

PostgreSQL中的类型处理(以及一般的SQL)是严格的。定义函数的RETURN类型可能很棘手 有简单的解决方案与纯SQL

选择并重命名所有列:

SELECT * FROM t AS t(id, name, email, salt);

选择并重命名一些列:

SELECT user_id AS id, user_name AS name FROM t;

与功能结合

如果由于某种原因需要服务器端功能,您仍然可以将这些SQL功能与功能结合使用。给出一个表格t,就像你的问题和这个简单的函数一样:

CREATE OR REPLACE FUNCTION f_test()
  RETURNS SETOF t AS
$func$
SELECT * FROM t  -- do stuff here
$func$ LANGUAGE sql STABLE;

仅重命名列(无需提供类型):

SELECT * FROM f_test() t(id, name, email, salt)

选择并重命名一些列:

SELECT user_id AS id, user_name AS name FROM f_test() t;

您可以将它与不同表格上的各种不同功能结合起来:
Refactor a PL/pgSQL function to return the output of various SELECT queries

但我不完全确定你想要解决哪个问题。

旁白

答案 1 :(得分:1)

我不认为这在pl / pgsql中是可行的,因为它强烈依赖于用户定义的类型。可悲的是,这种语言对于类型自动检测还不够聪明...我认为我将使用的第一个可能的解决方案,它解决了部分问题,因为至少我不需要通过表格列的类型更改手动重构每个函数

1。)询问列类型的可能解决方案:

CREATE FUNCTION test ()
  RETURNS TABLE (id "user".user_id%TYPE, name "user".user_name%TYPE, email "user".user_email%TYPE)
AS
  $BODY$
  BEGIN
    return QUERY SELECT
      "user".user_id, "user".user_name, "user".user_email
    FROM
      "user";
  END;
$BODY$
LANGUAGE 'plpgsql' VOLATILE;

至少这种类型不是多余的。不是最好的,但可以接受。

2.使用SETOF RECORD的可能解决方案:

CREATE FUNCTION test ()
  RETURNS SETOF RECORD
AS
  $BODY$
  BEGIN
    RETURN QUERY SELECT
      "user".user_id AS id, "user".user_name AS name, "user".user_email AS email
    FROM
      "user";
  END;
$BODY$
LANGUAGE 'plpgsql' VOLATILE;

没有列类型定义列表,我收到以下错误:

  

错误:返回函数需要列定义列表   “记录”

所以我必须像这样使用它:

SELECT * FROM test() AS (id INT, name VARCHAR, email VARCHAR);

而不是:

SELECT * FROM test()

我通过php客户端获取字符串中的每一列,因此列类型定义对我来说无用......这种解决方案在没有列类型定义的情况下是最好的,但是不可接受。

可以使用类似于表格的表格:

CREATE FUNCTION test (OUT id "user".user_id%TYPE, OUT name "user".user_name%TYPE, OUT email "user".user_email%TYPE)
  RETURNS SETOF RECORD
AS
  $BODY$
  BEGIN
    RETURN QUERY SELECT
      "user".user_id, "user".user_name, "user".user_email
    FROM
      "user";
  END;
$BODY$
LANGUAGE 'plpgsql' VOLATILE;

例如,我可以将所有内容设置为TEXT:

CREATE FUNCTION test (OUT id TEXT, OUT name TEXT , OUT email TEXT )
  RETURNS SETOF RECORD
AS
  $BODY$
  BEGIN
    RETURN QUERY SELECT
      "user".user_id::TEXT , "user".user_name::TEXT , "user".user_email::TEXT 
    FROM
      "user";
  END;
$BODY$
LANGUAGE 'plpgsql' VOLATILE;

这有效,但这远不是类型自动检测,它会导致很多冗余的文本转换器代码......

3.。)使用refcursors的可能解决方案:

CREATE FUNCTION test ()
  RETURNS SETOF "user"
AS
  $BODY$
  DECLARE
    refc "user";
  BEGIN
    FOR refc IN 
      SELECT
        "user".user_id, "user".user_name, "user".user_email
      FROM
        "user"
    LOOP
        RETURN NEXT refc;
    END LOOP ;
    RETURN ;
  END;
$BODY$
LANGUAGE 'plpgsql' VOLATILE;

这填补了缺少列的空值,我无法命名列,而且sql循环非常慢......所以这是不可接受的。

通过refcursors还有另一种方法:返回refcursor本身,但它是不可接受的,因为我不能将它用作普通变量,我必须给一个字符串作为游标名称...顺便说一句我没有设法使用refgsrsor本身就是phpstorm的结果。我找到了jdbc cursor not found错误。也许我把名字设置错了,我不知道,我认为这不值得付出更多的努力。