0

I'm new to PLSQL so I have a very basic question that I hope somebody will help me with. I would like to run a straight forward select statement against an 11g DB and then a slightly modified version of that SQL statement if it's a 12g database. The results should get outputted to the screen. The code below works until I add a SQL statement. I tried 'execute immediate' but that didn't work and then using a cursor - but for multiple columns and multiple rows that can't be the answer as it's so long winded. What am I missing here? The statements could be anything, like for example:

--11g
select NAME,OPEN_MODE,DBID from v$database; 
--12c
select NAME,OPEN_MODE,DBID,con_id from v$database;

I'd just like to know how to approach handling any SQL select statement (that returns multiple rows and cols) in such a code block. Thanks.

DECLARE
    v_dbver    BINARY_INTEGER;
BEGIN
    SELECT TO_NUMBER(SUBSTR(version,1,2)) INTO v_dbver FROM v$instance;
    DBMS_OUTPUT.PUT_LINE('Version ='||v_dbver);
IF v_dbver < 12 THEN
    DBMS_OUTPUT.PUT_LINE('Running against 11g');
    <SQL statement>
ELSIF v_dbver >= 12 THEN
    DBMS_OUTPUT.PUT_LINE('Running against 12c');
    <SQL statement>
END IF;
END;
/
2
  • 2
    You cannot use a bare select in a PL/SQL block -- it has to be either select into some variables which you then can print, or a cursor, as you have already figured out.
    – mustaccio
    Jun 23 '21 at 17:31
  • Thanks for that confirmation. Appreciate it.
    – Franco
    Jun 24 '21 at 10:17
1

Edit: Please see the additional notes at the bottom for how this can likely be done by using PIPELINE functions.

PL/SQL in ORACLE does not return Datasets in the same way that other RDBMS do (like SQL Server, where you can just pop a random SELECT statement anywhere in a block or procedure and it will output the results to the grid).

Oracle will only output to the screen as a standard SELECT query or via a refcursor.

What you're doing is known as an anonymous block. Anonymous Blocks do not display the results of a SELECT statement. They exist to "get work done." I wrote a blog post that explains this a bit, you're welcome to take a look: https://sqldevdba.com/f/t-sql-vs-plsql-series-part-2-select-into

For your issue: You have a few options, including:

  1. Output the Select statement using a RefCursor. (https://oracle-base.com/articles/misc/using-ref-cursors-to-return-recordsets)

2. Write the results to a table (Using Insert INTO ... SELECT FROM), then Select from that table when done. (This is my preferred approach when running scheduled items like reports). This will also be the approach that gets this "out the door" the fastest, as you'll then be able to query the results immediately.

  1. Printing each record to the DBMS_OUTPUT (yuck) using a resultset
  2. Use a cursor to work through each record of the resultset, and write the data to a csv file.

For option 2 (which I'd likely take but of course you don't have to), it would be something like:

--If you have an existing table (TableName) that you always want to use
DECLARE
    v_dbver    BINARY_INTEGER;
BEGIN
    SELECT TO_NUMBER(SUBSTR(version,1,2)) INTO v_dbver FROM v$instance;
    DBMS_OUTPUT.PUT_LINE('Version ='||v_dbver);
    
    --This table needs to exist already, so you'll have to have created it ahead of time
    TRUNCATE TABLE TableNameHere; --Truncate the table to remove artifacts
    
IF v_dbver < 12 THEN
    DBMS_OUTPUT.PUT_LINE('Running against 11g');
    --11g
    INSERT INTO TableName
    (NAME,OPEN_MODE,DBID)
    SELECT NAME,OPEN_MODE,DBID 
    FROM v$database; 
    
ELSIF v_dbver >= 12 THEN
    DBMS_OUTPUT.PUT_LINE('Running against 12c');
    --12c
    INSERT INTO TableName
    (NAME,OPEN_MODE,DBID,con_id)
    SELECT NAME,OPEN_MODE,DBID,con_id 
    FROM v$database;
END IF;
END;


--If you want it to DYNAMICALLY create the table:


DECLARE
    v_dbver    BINARY_INTEGER;
BEGIN
    SELECT TO_NUMBER(SUBSTR(version,1,2)) INTO v_dbver FROM v$instance;
    DBMS_OUTPUT.PUT_LINE('Version ='||v_dbver);
    
    --This table needs to exist already, so you'll have to have created it ahead of time
    TRUNCATE TABLE TableNameHere; --Truncate the table to remove artifacts
    
IF v_dbver < 12 THEN
    DBMS_OUTPUT.PUT_LINE('Running against 11g');
    --11g
    CREATE TABLE TableName_Timestamp --This will auto-create the table 
    AS
    SELECT NAME,OPEN_MODE,DBID 
    FROM v$database; 
    
ELSIF v_dbver >= 12 THEN
    DBMS_OUTPUT.PUT_LINE('Running against 12c');
    --12c
    CREATE TABLE TableName_Timestamp --This will auto-create the table 
    AS
    SELECT NAME,OPEN_MODE,DBID,con_id 
    FROM v$database;
END IF;
END;

EDIT!! Well, my world has been turned upside down. While this approach isn't perfect, it CERTAINLY changes things.

Return many rows on a plsql Oracle10g

This solution allows you to set up and deploy a function, and have that function return datasets from a SELECT statements. Give it a shot!!

4
  • 1
    Many thanks for the suggestions. You're idea to simply create table as select ... and then just select from that new table outside of the PLSQL block works. That's great and really quick.
    – Franco
    Jun 24 '21 at 0:17
  • Happy to help! Your question helped me as well. Brave new world of Pipeline functions out there that I'm going to go explore.
    – SQLDevDBA
    Jun 24 '21 at 18:49
  • 1
    If running against 11g, SELECT NAME,OPEN_MODE,DBID,con_id FROM v$database won't compile, so it will need to be dynamic. Jun 26 '21 at 10:04
  • @WilliamRobertson absolutely right, spot on. A simple EXECUTE IMMEDIATE should remedy that I imagine.
    – SQLDevDBA
    Jun 28 '21 at 16:39
0

PL/SQL isn't designed for writing queries, so as others have mentioned, you can't just select away and expect a result set. However, depending on the client tool you are using, you may be able to do this:

declare
    rc sys_refcursor;
begin
    if dbms_db_version.ver_le_12 then open rc for
        select name,open_mode,dbid from v$database; 
    else open rc for
        'select name,open_mode,dbid,con_id from v$database';
    end if;

    dbms_sql.return_result(rc);
end;

From a quick test, this works in SQL*Plus (12c or later) and SQL Developer. It doesn't work in PL/SQL Developer 14.0.6.

Bear in mind that if you are running against 11g, v$database has no con_id column, so the query has to be dynamic in order for it to compile. You could even construct a single entire query dynamically, rather than having two.


Edit: now that I think about it, this won't work for 11g anyway because 11g's dbms_sql has no return_result procedure, sorry. However, this approach could be useful in other situations that don't require legacy database versions. Pre-12c you could define a refcursor variable with var rc refcursor and print it after the block completes (works in SQL*Plus etc).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.