【问题标题】:Is it possible to output a SELECT statement from a PL/SQL block?是否可以从 PL/SQL 块输出 SELECT 语句?
【发布时间】:2010-09-25 23:23:22
【问题描述】:

如何获得一个 PL/SQL 块来输出 SELECT 语句的结果,就像我执行普通的 SELECT 一样?

例如如何做一个SELECT like:

SELECT foo, bar FROM foobar;

提示:

BEGIN
SELECT foo, bar FROM foobar;
END;

没用。

【问题讨论】:

  • 我觉得没有任何人回答了这个问题。我是一个 PL/SQL 菜鸟,当从 PL/SQL 块内部执行它时,我无法弄清楚如何从一个简单的选择语句中获取结果集以显示在 DBVisualizer 中。这可能是一个非常简单的答案,但作为一个 MSSQL 用户,我在这里迷路了。我已经尝试过谷歌搜索,但我没有偶然发现答案,否则我会自己添加。
  • Sergey 的回答非常全面,但您希望在类似的工具(SQL Developer vs DBVisualizer)中看到它的可视化,请查看thatjeffsmith.com/archive/2012/03/dbms_output-in-sql-developer
  • 如果您在单个 PL/SQL 块中工作,并且您可能正在使用 Oracle 的 SQL Developer 使用 dbms_output.put_line() 访问您的数据库,您会这样做。另一方面,如果您将此代码作为包或函数编译到 Oracle 数据库中,您将使用 PIPE ROW(r); 将数据输出到 PIPE ROW(r);

标签: sql oracle plsql oracle10g oracle-apex


【解决方案1】:

您可以在 Oracle 12.1 或更高版本中执行此操作:

declare
    rc sys_refcursor;
begin
    open rc for select * from dual;
    dbms_sql.return_result(rc);
end;

我没有要测试的 DBVisualizer,但这应该是你的起点。

有关更多详细信息,请参阅Oracle 12.1 New Features GuideOracle Base 等中的隐式结果集。

对于早期版本,根据工具的不同,您可能能够使用引用游标绑定变量,例如 SQL*Plus 中的此示例:

set autoprint on

var rc refcursor

begin
    open :rc for select count(*) from dual;
end;
/

PL/SQL procedure successfully completed.


  COUNT(*)
----------
         1

1 row selected.

【讨论】:

  • 谢谢!是否有另一种方法可以在 10.2 中提供相同的行为?我真正想做的就是弄清楚如何从 DBVisualizer 内部使用 SQL 变量运行查询。
  • DBVis 是否像 SQL*Plus 和 PL/SQL Developer 那样显示引用游标? (添加示例。)在 PL/SQL Developer 中,您将 PL/SQL 块输入到测试窗口中,并将rc 变量作为光标添加到下部面板中,然后在执行块后您可以单击该变量。
  • 我不确定,但即使不是特定于 DBVisiaulizer 的更多信息也会有所帮助,所以让我们选择“是”
  • 再次感谢,我会试试看! :)(赏金是你的。它会在 8 小时内解锁。)
  • 为什么第一个选项对我不起作用,输出 rc ?它执行,但没有显示结果。 Oracle 12c 企业版 12.1.0.2.0 - 64 位
【解决方案2】:

这取决于你需要什么结果。

如果您确定只有 1 行,请使用隐式游标:

DECLARE
   v_foo foobar.foo%TYPE;
   v_bar foobar.bar%TYPE;
BEGIN
   SELECT foo,bar FROM foobar INTO v_foo, v_bar;
   -- Print the foo and bar values
   dbms_output.put_line('foo=' || v_foo || ', bar=' || v_bar);
EXCEPTION
   WHEN NO_DATA_FOUND THEN
     -- No rows selected, insert your exception handler here
   WHEN TOO_MANY_ROWS THEN
     -- More than 1 row seleced, insert your exception handler here
END;

如果要选择多于 1 行,可以使用显式游标:

DECLARE
   CURSOR cur_foobar IS
     SELECT foo, bar FROM foobar;

   v_foo foobar.foo%TYPE;
   v_bar foobar.bar%TYPE;
BEGIN
   -- Open the cursor and loop through the records
   OPEN cur_foobar;
   LOOP
      FETCH cur_foobar INTO v_foo, v_bar;
      EXIT WHEN cur_foobar%NOTFOUND;
      -- Print the foo and bar values
      dbms_output.put_line('foo=' || v_foo || ', bar=' || v_bar);
   END LOOP;
   CLOSE cur_foobar;
END;

或使用其他类型的光标:

BEGIN
   -- Open the cursor and loop through the records
   FOR v_rec IN (SELECT foo, bar FROM foobar) LOOP       
   -- Print the foo and bar values
   dbms_output.put_line('foo=' || v_rec.foo || ', bar=' || v_rec.bar);
   END LOOP;
END;

【讨论】:

  • 另外,如果您使用 SQLPlus 来运行它,那么您需要 SET SERVEROUTPUT ON 才能看到输出。其他客户端可能有类似的选项需要启用。
【解决方案3】:

在包中创建一个函数并返回一个 SYS_REFCURSOR:

FUNCTION Function1 return SYS_REFCURSOR IS 
       l_cursor SYS_REFCURSOR;
       BEGIN
          open l_cursor for SELECT foo,bar FROM foobar; 
          return l_cursor; 
END Function1;

【讨论】:

    【解决方案4】:

    来自匿名区块?我现在想更多地了解您认为需要这样做的情况,因为对于子查询分解子句和内联视图,除了最复杂的情​​况之外,您很少需要求助于 PL/SQL。

    如果您可以使用命名过程,请使用流水线函数。这是从文档中提取的示例:

    CREATE PACKAGE pkg1 AS
      TYPE numset_t IS TABLE OF NUMBER;
      FUNCTION f1(x NUMBER) RETURN numset_t PIPELINED;
    END pkg1;
    /
    
    CREATE PACKAGE BODY pkg1 AS
    -- FUNCTION f1 returns a collection of elements (1,2,3,... x)
    FUNCTION f1(x NUMBER) RETURN numset_t PIPELINED IS
      BEGIN
        FOR i IN 1..x LOOP
          PIPE ROW(i);
        END LOOP;
        RETURN;
      END;
    END pkg1;
    /
    
    -- pipelined function is used in FROM clause of SELECT statement
    SELECT * FROM TABLE(pkg1.f1(5));
    

    【讨论】:

    • 从 Oracle 的角度来看,您是 100% 正确的。然而,在 Oracle 度过我的前 5 年,在 SQL Server 度过接下来的 5 年“奢侈”年,让我认为如果 Oracle 允许这样的事情,生活会更轻松[尽管在编程上不正确]。 :)
    【解决方案5】:

    如果您想在 pl/sql 中查看选择查询输出,您需要使用显式游标。它将保存活动数据集,并且通过一次获取每一行,它将显示活动数据集中的所有记录,只要它通过循环迭代从数据集中获取记录。此数据不会以表格格式生成,此结果将以纯文本格式生成。希望这会有所帮助。对于任何其他问题,您可能会问......

    set serveroutput on;
    declare
    cursor c1 is
       select foo, bar from foobar;
    begin
      for i in c1 loop
        dbms_output.put_line(i.foo || ' ' || i.bar);
      end loop;
    end;
    

    【讨论】:

      【解决方案6】:

      经典的“Hello World!”块包含一个可执行部分,该部分调用DBMS_OUTPUT.PUT_LINE 过程以在屏幕上显示文本:

      BEGIN
        DBMS_OUTPUT.put_line ('Hello World!');
      END;
      

      您可以在这里查看: http://www.oracle.com/technetwork/issue-archive/2011/11-mar/o21plsql-242570.html

      【讨论】:

      • 对不起,这仅适用于 Oracle 数据库它对我的 Pl/sql 块非常有帮助。
      【解决方案7】:

      对于 12c 以下的版本,简单的答案是 NO,至少不是 SQL Server。
      您可以打印结果,可以将结果插入表中,可以从函数/过程中将结果作为游标返回或从函数返回行集 -
      但是您不能执行 SELECT 语句,而不对结果进行任何处理。


      SQL 服务器

      begin
          select 1+1
          select 2+2
          select 3+3
      end
      

      /* 返回 3 个结果集 */


      甲骨文

      SQL> begin
        2  select * from dual;
        3  end;
        4  /
      select * from dual;
      *
      ERROR at line 2:
      ORA-06550: line 2, column 1:
      PLS-00428: an INTO clause is expected in this SELECT statement
      

      【讨论】:

        【解决方案8】:

        您需要使用本机动态 SQL。此外,您不需要 BEGIN-END 来运行 SQL 命令:

        declare
          l_tabname VARCHAR2(100) := 'dual';
          l_val1    VARCHAR2(100):= '''foo''';
          l_val2    VARCHAR2(100):= '''bar''';
          l_sql     VARCHAR2(1000);  
        begin
          l_sql:= 'SELECT '||l_val1||','||l_val2||' FROM '||l_tabname;
          execute immediate l_sql;
          dbms_output.put_line(l_sql);
        end;
        /
        
        Output:
         SELECT 'foo','bar' FROM dual
        

        【讨论】:

          【解决方案9】:

          使用立即执行语句

          喜欢:

          declare
           var1    integer;
          var2 varchar2(200)
          begin
           execute immediate 'select emp_id,emp_name from emp'
             into var1,var2;
           dbms_output.put_line(var1 || var2);
          end;
          

          【讨论】:

          • 这里不用动态sql了!应该是 select ... into ... 语句。
          【解决方案10】:

          即使问题很老,但我会分享完美回答问题的解决方案:

          SET SERVEROUTPUT ON;
          
          DECLARE
              RC SYS_REFCURSOR;
              Result1 varchar2(25);
              Result2 varchar2(25);
          BEGIN
              OPEN RC FOR SELECT foo, bar into Result1, Result2 FROM foobar;
              DBMS_SQL.RETURN_RESULT(RC);
          END;
          

          【讨论】:

            【解决方案11】:

            当您的选择查询返回多行时使用游标。因此,当您需要聚合或单行数据时,您可以使用没有游标的过程/函数,而不是使用游标

              Create Procedure sample(id 
                varchar2(20))as 
                Select count(*) into x from table 
                where 
                   Userid=id;
                 End ;
            

            然后简单地调用过程

               Begin
               sample(20);
               End
            

            这是过程/函数的实际使用,主要是包装和存储复杂的查询,或者需要使用相同的逻辑但不同的数据重复操作

            【讨论】:

              猜你喜欢
              • 2017-03-03
              • 2015-08-28
              • 2023-03-19
              • 2014-01-03
              • 1970-01-01
              • 1970-01-01
              • 2017-01-16
              • 1970-01-01
              • 2021-08-21
              相关资源
              最近更新 更多