如何从 PostgreSQL 存储过程中获取结果集?

Naf*_*dar 6 postgresql stored-procedures function postgresql-11

我在 PostgreSQL 11 中创建了一个存储过程来执行 CRUD 操作,它适用于 1. 创建 2. 更新 3. 删除,但是当我通过传递Condition = 4选择结果集来运行读取命令时,出现以下错误。

我已经使用 PostgreSQL 函数来获取对我有用的结果集,但是我需要使用 PostgreSQL 存储过程来获取结果。

这是我的存储过程代码:

CREATE OR REPLACE PROCEDURE public.testSpCrud(
    fnam text,
    lnam text,
    id integer,
    condition integer)
LANGUAGE 'plpgsql'

AS $BODY$
declare
    countOfDisc int; 
BEGIN
if condition=1 then

INSERT INTO public.employee(
    employeeid, lname, fname, securitylevel, employeepassword, hphonearea, hphone, cphonearea, cphone, street, city, state, zipcode, extzip, name, email, groomerid, type, commission, inactive, carrierid, notoallemployees, languageid, isdogwalker, ispetsitter, ismobilegroomer, ssma_timestamp)
    VALUES (4,  'Test', 'Test', 2, 2, 32, 32, 32, 32, 32, 32,32, 32, 32, 22, 22, 2, 2, 2, false, 223,true, 223, true, true, true, '2019-08-27');
end if;
 if condition =2 then
    delete from Employee where employeeid=id;
    end if;
     if condition =3 then
    update Employee set fname='Test' where employeeid=id;
    end if;
     if condition =4 then
         Select * from Employee;
    end if;
    END;
$BODY$;
Run Code Online (Sandbox Code Playgroud)
ERROR: query has no destination for result data
HINT: If you want to discard the results of a SELECT, use PERFORM instead.
CONTEXT: PL/pgSQL function testspcrud(text,text,integer,integer) line 22 at SQL statement
SQL state: 42601
Run Code Online (Sandbox Code Playgroud)

Erw*_*ter 7

截至 Postgres 13,从 a 返回PROCEDURE仍然非常有限。看:

\n\n

最有可能的是,您误会了广泛使用的误称“存储过程”,并且真正想要一个FUNCTION替代方案,它可以根据其声明返回一个值、一行或一组。

\n\n

会像这样工作:

\n
CREATE OR REPLACE FUNCTION public.testSpCrud(\n    fnam text,\n    lnam text,\n    id integer,\n    condition integer)\n  RETURNS SETOF Employee LANGUAGE plpgsql AS\n$func$\nBEGIN\n   CASE condition\n   WHEN 1 THEN\n      INSERT INTO public.employee(\n       employeeid, lname, fname, securitylevel, employeepassword, hphonearea, hphone, cphonearea, cphone, street, city, state, zipcode, extzip, name, email, groomerid, type, commission, inactive, carrierid, notoallemployees, languageid, isdogwalker, ispetsitter, ismobilegroomer, ssma_timestamp)\n       VALUES (4,  \'Test\', \'Test\', 2, 2, 32, 32, 32, 32, 32, 32,32, 32, 32, 22, 22, 2, 2, 2, false, 223,true, 223, true, true, true, \'2019-08-27\');\n\n   WHEN 2 THEN\n      DELETE FROM Employee WHERE employeeid=id;\n\n   WHEN 3 THEN\n      UPDATE Employee SET fname=\'Test\' WHERE employeeid=id;\n\n   WHEN 4 THEN\n      RETURN QUERY\n      SELECT * FROM Employee;\n\n   ELSE\n      RAISE EXCEPTION \'Unexpected condition value %!\', condition;\n   END CASE;\nEND\n$func$;\n
Run Code Online (Sandbox Code Playgroud)\n

使用构造进行简化CASE,并添加一个ELSE子句。适应您的需求。

\n

致电:

\n
SELECT * FROM public.testSpCrud(...);\n
Run Code Online (Sandbox Code Playgroud)\n

另外:plpgsql 块的所有变量名称在嵌套 SQL DML 命令中都是可见的。一个名为的变量id是一个等待发生的问题。我建议采用更安全的命名约定,和/或对 DML 语句中的所有列名称进行表限定。一种流行的命名约定是在变量名称前面添加下划线。喜欢:_id

\n

并考虑 SQL和PL/pgSQL中合法的小写标识符。

\n\n


Alc*_*att 6

大多数人会建议,当将 MS SQL Server 存储过程迁移到 PostgreSQL 时,如果该过程返回一组数据(行和列),则将存储过程更改为函数,因为函数本质上返回数据集。然而,从 Postgres 11 开始,您可以使用游标从 PostgreSQL 过程返回结果集,尽管迭代结果可能很乏味。

下面演示如何使用 INOUT 游标从 PostgreSQL 过程返回一组数据:

CREATE OR REPLACE PROCEDURE test_get_data_single(
    _itemID int, 
    INOUT _message text = '', 
    INOUT _result_one refcursor = 'rs_resultone',
    INOUT _returnCode text = '')
LANGUAGE plpgsql
AS
$$
BEGIN
    _message := 'Test message for item ' || COALESCE(_itemID, 0);
    _returnCode := '';

  open _result_one for 
    SELECT * 
    FROM (values (1,2,3, 'fruit', current_timestamp - INTERVAL '5 seconds'), 
                 (4,5,6, 'veggie', current_timestamp)) as t(a,b,c,d,e);

END;
$$;
Run Code Online (Sandbox Code Playgroud)

要使用,请在事务中调用该过程

BEGIN;
    CALL test_get_data_single(1);
    FETCH ALL FROM "rs_resultone";
COMMIT;
Run Code Online (Sandbox Code Playgroud)

PostgreSQL 还支持使用Begin/ End

BEGIN;
    CALL test_get_data_single(2);
    FETCH ALL FROM "rs_resultone";
END;
Run Code Online (Sandbox Code Playgroud)

DBeaver的示例结果

+--------------------------+--------------+-------------+
|    _message              | _result_one  | _returncode |
+--------------------------+--------------+-------------+
| Test message for item 2  | rs_resultone |             |
+--------------------------+--------------+-------------+
Run Code Online (Sandbox Code Playgroud)
+---+---+---+--------+---------------------|
| a | b | c | d      | e                   | 
+---+---+---+--------+---------------------|
| 1 | 2 | 3 | fruit  | 2020-02-15 10:12:09 | 
| 4 | 5 | 6 | veggie | 2020-02-15 10:12:09 |
+---+---+---+--------+---------------------|
Run Code Online (Sandbox Code Playgroud)

要对结果进行更高级的处理,请使用匿名代码块迭代结果

BEGIN;
    CALL test_get_data_single(2);
    FETCH ALL FROM "rs_resultone";
END;
Run Code Online (Sandbox Code Playgroud)

DBeaver的示例结果(查看服务器输出,使用 Ctrl+Shift+O 打开):

Cursor _result_single from test_get_data_single: <unnamed portal 261>
1, array: (1,2,3,fruit,"2020-02-14 17:19:29.612822-08")
1, values: 1  2  3  fruit
2, array: (4,5,6,veggie,"2020-02-14 17:19:34.612822-08")
2, values: 4  5  6  veggie
Run Code Online (Sandbox Code Playgroud)

psql的结果示例:

INFO:  Cursor _result_single from test_get_data_single: <unnamed portal 4>
INFO:  1, array: (1,2,3,fruit,"2020-02-14 17:22:50.81671-08")
INFO:  1, values: 1  2  3  fruit
INFO:  2, array: (4,5,6,veggie,"2020-02-14 17:22:55.81671-08")
INFO:  2, values: 4  5  6  veggie
Run Code Online (Sandbox Code Playgroud)

过程还可以使用单独的refcursor参数返回两个结果集:

+--------------------------+--------------+-------------+
|    _message              | _result_one  | _returncode |
+--------------------------+--------------+-------------+
| Test message for item 2  | rs_resultone |             |
+--------------------------+--------------+-------------+
Run Code Online (Sandbox Code Playgroud)

检索结果:

+---+---+---+--------+---------------------|
| a | b | c | d      | e                   | 
+---+---+---+--------+---------------------|
| 1 | 2 | 3 | fruit  | 2020-02-15 10:12:09 | 
| 4 | 5 | 6 | veggie | 2020-02-15 10:12:09 |
+---+---+---+--------+---------------------|
Run Code Online (Sandbox Code Playgroud)

或者使用扩展的匿名代码块来查看结果

DO
$$
DECLARE
    _message text = '';
    _returnCode text = '';
    _result_one refcursor;
    _result_two refcursor;
    _result_single refcursor;
    _currentRow record;
    _i int;
BEGIN

    CALL test_get_data(1, _message => _message, _result_one => _result_one, _result_two => _result_two, _returnCode => _returnCode);

    RAISE info '%', _message;

    RAISE info '';
    RAISE info 'Cursor _result_one: %', _result_one;

    _i := 0;
    WHILE TRUE
    Loop
        FETCH NEXT FROM _result_one INTO _currentRow;

        IF _currentRow IS NULL Then
            EXIT;
        END IF;

        _i := _i + 1;
        RAISE info '%, array: %', _i, _currentRow;
        RAISE info '%, values: %  %  %  %', _i, _currentRow.a, _currentRow.b, _currentRow.c, _currentRow.d;

    END LOOP;

    RAISE info '';
    RAISE info 'Cursor _result_two: %', _result_two;

    _i := 0;
    WHILE TRUE
    Loop
        FETCH NEXT FROM _result_two INTO _currentRow;

        IF _currentRow IS NULL Then
            EXIT;
        END IF;

        _i := _i + 1;
       RAISE info '%: %', _i, _currentRow;

    END LOOP;
End
$$;
Run Code Online (Sandbox Code Playgroud)

输出:

Test message for item 1

Cursor _result_one: <unnamed portal 263>
1, array: (1,2,3,fruit,"2020-02-14 17:25:06.528551-08")
1, values: 1  2  3  fruit
2, array: (4,5,6,veggie,"2020-02-14 17:25:11.528551-08")
2, values: 4  5  6  veggie

Cursor _result_two: <unnamed portal 264>
1: (one)
2: (two)
3: (three)
4: (four)
Run Code Online (Sandbox Code Playgroud)

另一种设计模式是使用语句,特别是对于通常添加/更新数据但偶尔想要预览结果的过程RAISE INFO。例如:

DO
$$
DECLARE
    _message text = '';
    _returnCode text = '';
    _result_one refcursor;
    _result_single refcursor;
    _currentRow record;
    _i int;
BEGIN

    CALL test_get_data_single(1, _message => _message, _result_one => _result_single, _returnCode => _returnCode);

    RAISE info 'Cursor _result_single from test_get_data_single: %', _result_single;

    _i := 0;
    WHILE TRUE
    Loop
        FETCH NEXT FROM _result_single INTO _currentRow;

        IF _currentRow IS NULL Then
            EXIT;
        END IF;

        _i := _i + 1;
        RAISE info '%, array: %', _i, _currentRow;
        RAISE info '%, values: %  %  %  %', _i, _currentRow.a, _currentRow.b, _currentRow.c, _currentRow.d;
    END LOOP;

End
$$;
Run Code Online (Sandbox Code Playgroud)

示例用法(完整过程位于GitHub 上的PNNL-Comp-Mass-Spec/DBSchema_PgSQL_DMS存储库中):

Cursor _result_single from test_get_data_single: <unnamed portal 261>
1, array: (1,2,3,fruit,"2020-02-14 17:19:29.612822-08")
1, values: 1  2  3  fruit
2, array: (4,5,6,veggie,"2020-02-14 17:19:34.612822-08")
2, values: 4  5  6  veggie
Run Code Online (Sandbox Code Playgroud)

结果示例:

+-----------------------------------+-------------+
|    _message                       | _returnCode |
+-----------------------------------+-------------+
| Would set 8 managers to Active;   |             |
| see the Output window for details |             |
+-----------------------------------+-------------+
Run Code Online (Sandbox Code Playgroud)

输出窗口内容:

State Change Preview   Parameter Name  Manager Name         Manager Type              Enabled (control_from_website=1)
False --> True         mgractive       Pub-80-1             Analysis Tool Manager     1                        
False --> True         mgractive       Pub-80-2             Analysis Tool Manager     1                        
False --> True         mgractive       Pub-80-3             Analysis Tool Manager     1                        
False --> True         mgractive       Pub-80-4             Analysis Tool Manager     1                        
False --> True         mgractive       Pub-80-5             Analysis Tool Manager     1                        
False --> True         mgractive       Pub-80-6             Analysis Tool Manager     1                        
False --> True         mgractive       Pub-80-7             Analysis Tool Manager     1                        
False --> True         mgractive       Pub-80-8             Analysis Tool Manager     1                        
Run Code Online (Sandbox Code Playgroud)