【问题标题】:Returning values from Dynamic PL/SQL to match a basic SELECT FROM statement从动态 PL/SQL 返回值以匹配基本的 SELECT FROM 语句
【发布时间】:2021-04-23 05:10:38
【问题描述】:

这无疑是一个非常基本的 Dynamic PL/SQL 问题,但我被卡住了。

我希望编写一个动态 PL/SQL,它的功能与下面的代码相同,从表名的所有行返回 field1、field2、field3 的值:

SELECT field1, field2, field3 FROM databasename.tablename;

如果我编写以下动态 SQL,我可以让它成功执行。但是,我无法让它返回任何东西:

declare
    Query  VARCHAR2(200) := 'SELECT field1, field2, field3 FROM databasename.tablename';
begin
    EXECUTE IMMEDIATE Query;
end;

请问动态SQL的返回结果如何匹配上面一行的SELECT语句?

【问题讨论】:

    标签: sql oracle plsql dynamic-sql


    【解决方案1】:
    • 您的第一个 select 语句是 sql 查询,它可以以表格格式返回结果。

    • 但是您的第二个代码是anonymous block,如果没有INTO 子句,您将无法在匿名块中执行任何选择查询。

    如果您知道您的选择查询将为您提供单条记录,请使用INTO 子句,如下所示:

    EXECUTE IMMEDIATE Query into var1, var2, var3;
    

    注意:在声明部分用正确的数据类型声明变量var1, var2 and var3

    【讨论】:

    • 好的,谢谢。但是,正如我在原始问题中所问的那样,如何返回所有行?有没有办法从动态 SQL 返回表格数据以匹配第一个 select 语句的输出?
    【解决方案2】:

    为您的案例从查询字符串返回数据集的简单方法是在存储函数或过程中使用返回类型为SYS_REFCURSOR 的游标,例如

    CREATE OR REPLACE FUNCTION Get_Result_Of_TheTable RETURN SYS_REFCURSOR IS
      Query       VARCHAR2(200) := 'SELECT field1, field2, field3 FROM tablename';
      v_recordset SYS_REFCURSOR;
    BEGIN
      OPEN v_recordset FOR Query;
      RETURN v_recordset;
    END;
    /
    

    然后从 SQL Developer 的控制台调用

    DECLARE
        result SYS_REFCURSOR;
    BEGIN
       :result := Get_Result_Of_TheTable;
    END;
    /
    
    PRINT result;
    

    【讨论】:

      【解决方案3】:

      有几种可能性。如果知道返回了哪些字段,那么可以这样解决:

      DECLARE
         TYPE cursor_type IS REF CURSOR;
         l_cursor cursor_type;
         l_record tablename%ROWTYPE;
         l_query  VARCHAR2(32767) := 'SELECT field1, field2, field3 FROM tablename';
      BEGIN
         OPEN l_cursor FOR l_query;
         LOOP
            FETCH l_cursor INTO l_record;
            EXIT WHEN l_cursor%NOTFOUND;
            
            DBMS_OUTPUT.PUT_LINE (l_record.field1 ||' '|| l_record.field2 ||' '|| l_record.field3);
         END LOOP;
         
         CLOSE l_cursor;
      END;
      /
      

      或者你可以写一个小的流水线函数:

      CREATE OR REPLACE PACKAGE pkg_test
      AS
         TYPE test_type IS TABLE
            OF tablename%ROWTYPE;
            
         FUNCTION get_fields
            RETURN test_type PIPELINED;
      END pkg_test;
      /
      
      CREATE OR REPLACE PACKAGE BODY pkg_test
      AS    
         FUNCTION get_fields
            RETURN test_type PIPELINED
         AS
            TYPE cursor_type IS REF CURSOR;
            l_cursor cursor_type;
            l_record tablename%ROWTYPE;
            l_query  VARCHAR2(32767) := 'SELECT field1, field2, field3 FROM tablename';
         BEGIN
            OPEN l_cursor FOR l_query;
            LOOP
               FETCH l_cursor INTO l_record;
               EXIT WHEN l_cursor%NOTFOUND;
               
               PIPE ROW (l_record);
            END LOOP;
         CLOSE l_cursor;
            
         END;
      END pkg_test;
      /
      
      SELECT * FROM TABLE(pkg_test.get_fields);
      

      这是为了我的测试:

      CREATE TABLE tablename
      ( field1 VARCHAR2(10),
        field2 VARCHAR2(10),
        field3 VARCHAR2(10)
      );
      
      INSERT INTO tablename VALUES ('A1', 'A2', 'A3');
      INSERT INTO tablename VALUES ('B1', 'B2', 'B3');
      INSERT INTO tablename VALUES ('C1', 'C2', 'C3');
      
      COMMIT;
      

      【讨论】:

        【解决方案4】:

        您可以使用bulk collect into 子句,例如用于 PL/SQL 块中的静态 SQL。

        这是一个简单的例子:

        drop table users_tb;
        
        create table users_tb (
          id           integer      generated always as identity,
          --
          username     varchar2(30) not null,
          is_active    char(1)      default 1 not null,
          created_date date         default sysdate not null ,
          edited_date  date,
          --
          constraint user_id_pk        primary key (id),
          constraint user_is_active_ch check (is_active in (1,0))
        );
        
        insert into users_tb (username) values ('john.wick');
        insert into users_tb (username) values ('constantine');
        insert into users_tb (username) values ('neo');
        commit;
        
        declare
          -- types
          type tr_list is record (
            username  varchar2(30),
            is_active integer
          );
          type tt_usernamelist is table of tr_list
            index by pls_integer;
                  
          -- variables
          lt_users tt_usernamelist;
          l_stmt   varchar2(2000);
        begin
          -- select statement
          l_stmt := 'select username, is_active from users_tb';
                             
          -- execution of dynamic code
          execute immediate l_stmt 
            bulk collect into lt_users;
          
          -- loop over retrived data
          for i in 1..lt_users.count loop
            dbms_output.put_line('User: '||lt_users(i).username||' - Is active: '||lt_users(i).is_active);
          end loop;
        end;
        

        重要的部分是我使用bulk collect intoexecution of dynamic code。这是DBFiddle 的链接,供您试用。

        【讨论】:

          猜你喜欢
          • 2013-06-14
          • 1970-01-01
          • 2011-04-20
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2010-09-25
          • 1970-01-01
          • 2012-05-09
          相关资源
          最近更新 更多