如何取消管道排?

how to cancel Pipe row?

我有管道功能。如果我发现异常,我想 return 没有行。即使在第一次使用管道排后发生异常。

CREATE TYPE a_r IS OBJECT (a1 VARCHAR (1), a2 VARCHAR (1));

CREATE TYPE a_t IS TABLE OF a_r;

CREATE or replace FUNCTION get_a
    RETURN a_t
    PIPELINED
IS
BEGIN
    FOR c IN (SELECT '1' a , '2' b FROM DUAL)
    LOOP
        PIPE ROW (a_r(c.a,c.b));
    END LOOP;
    FOR a
            IN (SELECT 'a2' a,
                       'b' b
                  FROM DUAL)
        LOOP
            PIPE ROW (a_r(a.a,a.b));
        END LOOP;
exception
        WHEN VALUE_ERROR
        THEN
            DBMS_OUTPUT.put_line ('VALUE_ERROR');
        WHEN OTHERS
        THEN
            DBMS_OUTPUT.put_line ('other');
    
END;

select * from table(get_a())

在此示例中,在错误 ('ab' is not varchar2(1)) 发生之前,第一行已通过管道传输。但我不想 returned。有没有办法写取消异常块中的管道?

code

否,因为客户端可能已经使用了该行。

您可以对您的函数进行编码,以便它用数据填充本地集合,并且仅在最后决定是否遍历集合以实际 return 行。这将需要更多内存,因为您在 returning 之前实现了整个结果集,并且会破坏使用流水线 table 函数的一些性能优势。

像这样的东西似乎可以做你想做的事

CREATE or replace FUNCTION get_a
    RETURN a_t
    PIPELINED
IS
  l_a_t a_t := new a_t();
BEGIN
    FOR c IN (SELECT '1' a , '2' b FROM DUAL)
    LOOP
        l_a_t.extend;
        l_a_t( l_a_t.count ) := a_r(c.a,c.b);
    END LOOP;
    FOR a
            IN (SELECT 'a2' a,
                       'b' b
                  FROM DUAL)
        LOOP
            l_a_t.extend;
            l_a_t( l_a_t.count ) := a_r(a.a,a.b);
        END LOOP;
        
   for i in 1 .. l_a_t.count
   loop
     pipe row( l_a_t(i) );
   end loop;
exception
        WHEN VALUE_ERROR
        THEN
            DBMS_OUTPUT.put_line ('VALUE_ERROR');
        WHEN OTHERS
        THEN
            DBMS_OUTPUT.put_line ('other');
    
END;
/

this dbfiddle