【问题标题】:Return setof record (virtual table) from function从函数返回记录集(虚拟表)
【发布时间】:2010-10-31 14:36:00
【问题描述】:

我需要一个 Postgres 函数来返回一个包含自定义内容的虚拟表(如在 Oracle 中)。该表将有 3 列和未知的行数。

我只是在互联网上找不到正确的语法。

想象一下:

CREATE OR REPLACE FUNCTION "public"."storeopeninghours_tostring" (numeric)
  RETURNS setof record AS
DECLARE
  open_id ALIAS FOR $1;
  returnrecords setof record;
BEGIN
  insert into returnrecords('1', '2', '3');
  insert into returnrecords('3', '4', '5');
  insert into returnrecords('3', '4', '5');
  RETURN returnrecords;
END;

如何正确书写?

【问题讨论】:

    标签: sql postgresql stored-procedures plpgsql


    【解决方案1】:

    所有以前存在的答案都已过时或开始时效率低下。

    假设您要返回三个integer 列。

    PL/pgSQL 函数

    以下是使用现代 PL/pgSQL(PostgreSQL 8.4 或更高版本)的方法:

    CREATE OR REPLACE FUNCTION f_foo() -- (open_id numeric) -- parameter not used
      RETURNS TABLE (a int, b int, c int) AS
    $func$
    BEGIN
    RETURN QUERY VALUES
      (1,2,3)
    , (3,4,5)
    , (3,4,5)
    ;
    END
    $func$  LANGUAGE plpgsql IMMUTABLE ROWS 3;
    

    在 Postgres 9.6 或更高版本中,您还可以添加 PARALLEL SAFE

    呼叫:

    SELECT * FROM f_foo();
    

    要点

    • 使用RETURNS TABLE 定义要返回的临时行类型。
      RETURNS SETOF mytbl 使用预定义的行类型。

    • 使用RETURN QUERY 通过一个命令返回多行。

    • 使用VALUES 表达式手动输入多行。这是标准 SQL,并且一直存在

    • 如果确实需要参数,请使用参数名称(open_id numeric) 而不是ALIAS, which is discouraged。在示例中,没有使用参数,只是噪音......

    • 不需要双引号完全合法的标识符。双引号仅用于强制其他非法名称(大小写混合、非法字符或保留字)。

    • Function volatility can be IMMUTABLE,因为结果永远不会改变。

    • ROWS 3 是可选的,但由于我们知道返回了多少行,我们不妨将它声明给 Postgres。可以帮助查询计划者选择最佳计划。

    简单的 SQL

    对于像这样的简单情况,您可以改用普通的 SQL 语句:

    VALUES (1,2,3), (3,4,5), (3,4,5)
    

    或者,如果您想(或必须)定义特定的列名和类型:

    SELECT *
    FROM  (
       VALUES (1::int, 2::int, 3::int)
            , (3, 4, 5)
            , (3, 4, 5)
       ) AS t(a, b, c);
    

    SQL 函数

    您可以将其包装成一个简单的SQL function

    CREATE OR REPLACE FUNCTION f_foo()
       RETURNS TABLE (a int, b int, c int) AS
    $func$
       VALUES (1, 2, 3)
            , (3, 4, 5)
            , (3, 4, 5);
    $func$  LANGUAGE sql IMMUTABLE ROWS 3;
    

    【讨论】:

      【解决方案2】:

      (这都是用 postgresql 8.3.7 测试的——你有更早的版本吗?看看你对“ALIAS FOR $1”的使用)

      CREATE OR REPLACE FUNCTION storeopeninghours_tostring(numeric)
       RETURNS SETOF RECORD AS $$
      DECLARE
       open_id ALIAS FOR $1;
       result RECORD;
      BEGIN
       RETURN QUERY SELECT '1', '2', '3';
       RETURN QUERY SELECT '3', '4', '5';
       RETURN QUERY SELECT '3', '4', '5';
      END
      $$;
      

      如果您有要返回的记录或行变量(而不是查询结果),请使用“RETURN NEXT”而不是“RETURN QUERY”。

      要调用该函数,您需要执行以下操作:

      select * from storeopeninghours_tostring(1) f(a text, b text, c text);
      

      因此,您必须定义您期望函数的输出行模式在查询中的内容。为避免这种情况,您可以在函数定义中指定输出变量:

      CREATE OR REPLACE FUNCTION storeopeninghours_tostring(open_id numeric, a OUT text, b OUT text, c OUT text)
       RETURNS SETOF RECORD LANGUAGE 'plpgsql' STABLE STRICT AS $$
      BEGIN
       RETURN QUERY SELECT '1'::text, '2'::text, '3'::text;
       RETURN QUERY SELECT '3'::text, '4'::text, '5'::text;
       RETURN QUERY SELECT '3'::text, '4'::text, '5'::text;
      END
      $$;
      

      (不太清楚为什么需要额外的 ::text 强制转换...'1' 默认情况下可能是 varchar?)

      【讨论】:

      • select * from storeopeninghours_tostring(1) f(a text, b text, c text); "f(" 是做什么的?我尝试用 d 和其他字符替换 f,它们似乎都可以工作....
      • f( 只是从函数中命名结果集——就像在“from”子句中为表起别名一样。
      【解决方案3】:

      我在我的函数中使用了很多临时表。您需要在数据库上创建一个返回类型,然后创建一个该类型的变量来返回。下面是执行此操作的示例代码。

      CREATE TYPE storeopeninghours_tostring_rs AS
      (colone text,
       coltwo text,
       colthree text
      );
      
      CREATE OR REPLACE FUNCTION "public"."storeopeninghours_tostring" () RETURNS setof storeopeninghours_tostring_rs AS
      $BODY$
      DECLARE
        returnrec storeopeninghours_tostring_rs;
      BEGIN
          BEGIN 
              CREATE TEMPORARY TABLE tmpopeninghours (
                  colone text,
                  coltwo text,
                  colthree text
              );
          EXCEPTION WHEN OTHERS THEN
              TRUNCATE TABLE tmpopeninghours; -- TRUNCATE if the table already exists within the session.
          END;
          insert into tmpopeninghours VALUES ('1', '2', '3');
          insert into tmpopeninghours VALUES ('3', '4', '5');
          insert into tmpopeninghours VALUES ('3', '4', '5');
      
          FOR returnrec IN SELECT * FROM tmpopeninghours LOOP
              RETURN NEXT returnrec;
          END LOOP;
      END;
      $BODY$
      LANGUAGE 'plpgsql' VOLATILE;
      
      
      select * from storeopeninghours_tostring()
      

      【讨论】:

      • 我喜欢这个解决方案。然后您可以轻松地在返回之前对结果进行排序等。
      【解决方案4】:
      CREATE OR REPLACE FUNCTION foo(open_id numeric, OUT p1 varchar, OUT p2 varchar, OUT p3 varchar) RETURNS SETOF RECORD AS $$
      BEGIN
        p1 := '1'; p2 := '2'; p3 := '3';
        RETURN NEXT; 
        p1 := '3'; p2 := '4'; p3 := '5';
        RETURN NEXT; 
        p1 := '3'; p2 := '4'; p3 := '5';
        RETURN NEXT; 
        RETURN;
      END;
      $$ LANGUAGE plpgsql;
      

      【讨论】:

        【解决方案5】:

        对于那些已经登陆这里寻找 MSSQL 等效于创建临时表并将其记录作为您的返回的人......这在 PostgreSQL 中不存在:( - 您必须定义返回类型。有两个在创建函数时或创建查询时执行此操作的方法。

        请看这里: http://wiki.postgresql.org/wiki/Return_more_than_one_row_of_data_from_PL/pgSQL_functions

        【讨论】:

          【解决方案6】:

          从 PostgreSQL 11 开始,您可以使用procedure

          CREATE OR REPLACE PROCEDURE f_foo()  LANGUAGE plpgsql
             AS
          $$
          BEGIN
          drop table if exists foo cascade;
          create temp table  IF NOT EXISTS foo (a int, b int, c int);
          insert into foo VALUES
            (1,2,3)
          , (3,4,5)
          , (3,4,5)
          ;
          END
          $$;
          

          当你想要一个临时表时,你可以调用它。

          call f_foo();
          

          如果存在表 foo,它将删除它。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2011-04-19
            • 1970-01-01
            • 2020-11-18
            • 2017-10-14
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多