PostgreSQL 存储过程返回行或空集

boo*_*biq 7 postgresql dynamic-sql plpgsql functions

我有一个存储过程,RETURNS SETOF ct_custom_type我在里面做

RETURN QUERY EXECUTE 'some dynamic query'
Run Code Online (Sandbox Code Playgroud)

我想这样做:如果这个“动态查询”返回 >= 10 行,我想返回它们,但如果它只返回 < 10 行,我不想返回任何东西(空集ct_custom_type)。

我试过:

RETURN QUERY EXECUTE 'some dynamic query'

GET DIAGNOSTICS variable = ROW_COUNT;
IF variable < 10 THEN

    # I don't know what to do here or how to accomplish this

END IF;
Run Code Online (Sandbox Code Playgroud)

如果我RETURN QUERY SELECT 0, 0, ''::text;IF块中执行(因为ct_custom_type是 的复合类型(integer, integer, text),它只是将这个“空行”添加到先前的查询结果中,但在这种情况下我不想返回任何内容,我可以这样做RETURN;,但它会返回先前的结果,我想丢弃它。

我有这样的:

EXECUTE 'dynamic query';

GET DIAGNOSTICS variable = ROW_COUNT;
IF variable >= 10 THEN
    RETURN QUERY EXECUTE 'dynamic query';
END IF;
Run Code Online (Sandbox Code Playgroud)

它有效,但我不想两次执行此查询。

dez*_*zso 8

您可以按照以下方式做一些事情:

test=> CREATE OR REPLACE FUNCTION temptabl(cnt integer)
RETURNS SETOF integer AS
$body$
BEGIN
    CREATE TEMPORARY TABLE tmp_container ON COMMIT DROP AS
    SELECT a
    FROM generate_series(1, cnt) t(a);

    IF (SELECT count(1) FROM tmp_container) > 5
    THEN
        RETURN QUERY SELECT a FROM tmp_container;
    END IF;
END;
$body$
LANGUAGE plpgsql;


test=> SELECT * FROM temptabl(4);
 temptabl 
----------
(0 rows)

test=> SELECT * FROM temptabl(6);
 temptabl 
----------
        1
        2
        3
        4
        5
        6
(6 rows)
Run Code Online (Sandbox Code Playgroud)

这样您只需执行一次原始查询。所有其他语句都适用于临时表。


Erw*_*ter 6

返回集

根据文档:

RETURN NEXT并且RETURN QUERY实际上并不从函数返回——它们只是将零或更多行附加到函数的结果集中。然后继续执行 PL/pgSQL 函数中的下一条语句。随着连续RETURN NEXTRETURN QUERY命令的执行,结果集被建立起来。finalRETURN应该没有参数,导致控制退出函数(或者你可以让控制到达函数的末尾)。

提高EXCEPTION替代

所以你可以通过提高 an 来取消操作,EXCEPTION客户端不会得到任何行。
不会比这更便宜:

CREATE OR REPLACE FUNCTION f_min_records(min_ct integer = 10)  -- default minimum 10
  RETURNS SETOF tbl AS
$func$
DECLARE
   row_ct int;
BEGIN
   RETURN QUERY EXECUTE 'some dynamic query (matching return type)';

   GET DIAGNOSTICS row_ct = ROW_COUNT;

   IF row_ct < min_ct THEN
      RAISE EXCEPTION 'Only % rows! Requested minimum was %.', row_ct, min_ct;
   END IF;
END
$func$  LANGUAGE plpgsql;
Run Code Online (Sandbox Code Playgroud)

调用(默认最少 10 行):

SELECT * FROM f_min_records_wrapper();
Run Code Online (Sandbox Code Playgroud)

我们在 ( get_available_flightid())的手册中包含了一个代码示例

不要提高 EXCEPTION

据我所知,防止返回集实际返回的唯一方法是引发异常。如果您也不想引发异常,那么您对当前的 plpgsql 有点不满意。

  • 如果在同一个函数中捕获EXCEPTION,仍会返回结果集。

  • 我试图用嵌套块解决它但失败了。似乎对返回集没有任何影响。

  • 但是您可以将函数调用嵌套在外部函数中并在那里捕获异常。这可以正常工作:

除了上面的扩展功能:

CREATE OR REPLACE FUNCTION f_min_records(min_ct integer = 10)  -- default minimum 10
  RETURNS SETOF t AS
$func$
DECLARE
   row_ct int;
BEGIN
   RETURN QUERY EXECUTE 'SELECT * from t';   -- some dynamic query (matching return type)

   GET DIAGNOSTICS row_ct = ROW_COUNT;

   IF row_ct < min_ct THEN
      RAISE SQLSTATE 'BRRRR'   -- 5 ASCII chars
      USING MESSAGE = format('Only %s rows! Requested minimum was %s.', row_ct, min_ct);
   END IF;

END
$func$  LANGUAGE plpgsql;
Run Code Online (Sandbox Code Playgroud)

创建一个包装函数,您实际调用它:

CREATE OR REPLACE FUNCTION f_min_records_wrapper(min_ct integer = 10)
  RETURNS SETOF t AS
$func$
BEGIN
   RETURN QUERY
   SELECT * from f_min_records(min_ct);

EXCEPTION 
   WHEN SQLSTATE 'BRRRR' THEN
   RAISE NOTICE '%', SQLERRM;  -- optionally pass error msg
END
$func$  LANGUAGE plpgsql;
Run Code Online (Sandbox Code Playgroud)

称呼:

SELECT * FROM f_min_records_wrapper(17);
Run Code Online (Sandbox Code Playgroud)

替代临时表

deszo 的答案相同的基本思想,但避免单独计数,以及其他功能:

CREATE OR REPLACE FUNCTION f_temptbl(min_ct integer = 10)
  RETURNS SETOF t AS
$func$
DECLARE
   row_ct int;
BEGIN
   DROP TABLE IF EXISTS _temptbl;                      -- for mult. calls in 1 transaction
   CREATE TEMP TABLE _temptbl (LIKE t) ON COMMIT DROP; -- match RETURNS type

   EXECUTE 'INSERT INTO _temptbl SELECT * FROM t';     -- text with dyn SQL

   GET DIAGNOSTICS row_ct = ROW_COUNT;

   IF row_ct >= min_ct THEN
      RETURN QUERY TABLE _temptbl;
   END IF;
END;
$func$  LANGUAGE plpgsql;
Run Code Online (Sandbox Code Playgroud)

SQL Fiddle演示了所有内容。

Feature Wish - 目前不存在(包括 9.4)

取消返回集的命令会很棒:

RETURN CANCEL;
Run Code Online (Sandbox Code Playgroud)

或者甚至可以选择“回滚”行数(默认为全部):

RETURN CANCEL 10;
Run Code Online (Sandbox Code Playgroud)