71

How can I get a PL/SQL block to output the results of a SELECT statement the same way as if I had done a plain SELECT?

For example how to do a SELECT like:

SELECT foo, bar FROM foobar;

Hint :

BEGIN
SELECT foo, bar FROM foobar;
END;

doesn't work.

Dominik
  • 966
  • 9
  • 28
GameFreak
  • 2,652
  • 5
  • 30
  • 38
  • 6
    I don't feel like any of the answered answered this question. I'm a PL/SQL noob and I'm having trouble figuring out how to get the result set from a simple select statement to show up in DBVisualizer when it's being executed from inside a PL/SQL block. It may be a pretty easy answer but as an MSSQL user I'm lost here. I have tried Googling a bit but I haven't stumbled upon the answer otherwise I'd add it myself. – Spencer Ruport Nov 01 '16 at 03:59
  • Sergey's answer is pretty comprehensive, but you would like to see it visualised in an analogous tool (SQL Developer vs DBVisualizer), check this out http://www.thatjeffsmith.com/archive/2012/03/dbms_output-in-sql-developer/ – Scott Nov 01 '16 at 08:33
  • If you are working within a single PL/SQL block and perhaps you are using Oracle's SQL Developer to access your database using `dbms_output.put_line()` is how you would do this. On the other hand if you are compiling this code into your Oracle Databse as a Package or a Function you would `PIPE` the data out using `PIPE ROW(r);` – Code Novice May 02 '19 at 16:28

11 Answers11

46

You can do this in Oracle 12.1 or above:

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

I don't have DBVisualizer to test with, but that should probably be your starting point.

For more details, see Implicit Result Sets in the Oracle 12.1 New Features Guide, Oracle Base etc.

For earlier versions, depending on the tool you might be able to use ref cursor bind variables like this example from 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.
William Robertson
  • 12,552
  • 3
  • 33
  • 36
  • Thank you! Is there another approach that would provide the same behavior in 10.2? Really all I'm trying to do is figure out how to run queries with SQL variables from inside DBVisualizer. – Spencer Ruport Nov 01 '16 at 17:32
  • Does DBVis display ref cursors like SQL*Plus and PL/SQL Developer? (Added example.) In PL/SQL Developer you'd type the PL/SQL block into a Test window and add the `rc` variable as a cursor in the lower panel, then after executing the block you could click on the variable. – William Robertson Nov 01 '16 at 17:36
  • I'm not sure but more info would be helpful even if it's not specific to DBVisiaulizer so let's go with "yes" – Spencer Ruport Nov 01 '16 at 17:38
  • Thanks again I'll give that a try! :) (Bounty is yours. It unlocks in 8 hours.) – Spencer Ruport Nov 01 '16 at 19:10
  • Why first option doesn't work for me, output of rc ? It executes, but show no result. Oracle 12c Enterprise 12.1.0.2.0 - 64bit – TPAKTOPA Feb 04 '21 at 09:31
  • @TPAKTOPA what client tool are you using? The tool has to be aware of this feature. – William Robertson Feb 04 '21 at 15:09
  • @WilliamRobertson TOAD, but I found a reason for strange behavior. I saved my temporary SQL's in a file with .SQL extension. Looks like that confused TOAD, and it doesn't worked properly with single SQL's. Opening new editor window, paste code there - all works fine. Also my TOAD is many years behind version - restrictions to update software on working machines. – TPAKTOPA Feb 16 '21 at 22:49
45

It depends on what you need the result for.

If you are sure that there's going to be only 1 row, use implicit cursor:

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;

If you want to select more than 1 row, you can use either an explicit cursor:

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;

or use another type of cursor:

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;
Sergey Stadnik
  • 2,850
  • 7
  • 24
  • 29
  • 5
    Also, if you are using SQLPlus to run this then you need to SET SERVEROUTPUT ON in order to see the output. Other clients may have similar options that need to be enabled. – Dave Costa Dec 09 '08 at 15:13
8

Create a function in a package and return a 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;
zygimantus
  • 3,212
  • 2
  • 35
  • 46
Igor Zelaya
  • 4,007
  • 4
  • 32
  • 50
8

From an anonymous block? I'd like to now more about the situation where you think that to be required, because with subquery factoring clauses and inline views it's pretty rare that you need to resort to PL/SQL for anything other than the most complex situations.

If you can use a named procedure then use pipelined functions. Here's an example pulled from the documentation:

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));
David Aldridge
  • 48,793
  • 8
  • 60
  • 88
  • 2
    From Oracle point of view, you're 100% correct. However, spending my first 5 years with Oracle and having the next 5 'luxury' years with SQL Server makes me think that life would be easier [though not programmatically correct] if Oracle would allow such thing. :) – tjeloep Aug 05 '15 at 12:13
5

if you want see select query output in pl/sql you need to use a explicit cursor. Which will hold active data set and by fetching each row at a time it will show all the record from active data set as long as it fetches record from data set by iterating in loop. This data will not be generated in tabular format this result will be in plain text format. Hope this will be helpful. For any other query you may ask....

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;
Ahsan Habib
  • 92
  • 1
  • 4
4

For versions below 12c, the plain answer is NO, at least not in the manner it is being done is SQL Server.
You can print the results, you can insert the results into tables, you can return the results as cursors from within function/procedure or return a row set from function -
but you cannot execute SELECT statement, without doing something with the results.


SQL Server

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

/* 3 result sets returned */


Oracle

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
David דודו Markovitz
  • 31,018
  • 5
  • 48
  • 69
4

The classic “Hello World!” block contains an executable section that calls the DBMS_OUTPUT.PUT_LINE procedure to display text on the screen:

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

You can checkout it here: http://www.oracle.com/technetwork/issue-archive/2011/11-mar/o21plsql-242570.html

Andreas
  • 4,832
  • 6
  • 40
  • 49
Dinesh Katwal
  • 872
  • 14
  • 24
2

You need to use Native dynamic SQL. Also, you do not need BEGIN-END to run SQL command:

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
Art
  • 5,106
  • 1
  • 18
  • 20
2

use execute immediate statement

like:

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;
  • 4
    there is no need to use dynamic sql here! It should be select ... into ... statement. – Rusty Nov 08 '16 at 15:02
0

Even if the question is old but i will share the solution that answers perfectly the question :

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;
Issam El omri
  • 101
  • 1
  • 3
0

Cursors are used when your select query returns multiple rows. So, rather using cursor in case when you want aggregates or single rowdata you could use a procedure/function without cursor as well like

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

And then simply call the procedure

   Begin
   sample(20);
   End

This is the actual use of procedure/function mostly wrapping and storing queries that are complex or that requires repeated manipulation with same logic but different data

Himanshu Ahuja
  • 2,849
  • 2
  • 5
  • 25