【问题标题】:plpgsql - column type auto detect by returning a resultsetplpgsql - 通过返回结果集自动检测列类型
【发布时间】:2013-07-02 03:30:06
【问题描述】:

我得到了 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_前缀并设置确切的列名,但我必须手动设置列类型。

是否可以兼得两者的优点?

【问题讨论】:

    标签: sql postgresql types plpgsql


    【解决方案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 客户端获取了字符串中的每一列,所以列类型定义对我来说毫无用处......这个解决方案如果没有列类型定义是最好的,但它不可接受。

    可以用这个类似于table:

    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 本身,但这是不可接受的,因为我不能将它用作普通变量,我必须给出一个字符串作为游标名称......顺便说一句我没有设法使用refcursor 本身作为 phpstorm 的结果。我得到 jdbc cursor not found 错误。可能是我把名字弄错了,我不知道,我觉得不值得再努力。

    【讨论】:

      【解决方案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

      但我不完全确定您想在这里特别解决哪个问题。

      旁白

      【讨论】:

      • 感谢您的回答和建议!我的目标是仅使用 SELECT * FROM func() 调用函数,因为我不想在客户端中使用 SQL 模板,而只是使用与数据库中存在的相同方法的代理。例如: $proxy->user_read_all() 应该返回一个包含用户的结果集。所以 select 语句中的列定义列表对我不利,因为它因每个函数而异......遗憾的是我无法通过这个项目选择数据库的版本。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-12-04
      • 2010-10-30
      • 2019-12-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多