-2
create or replace PROCEDURE BUILDREPORT_sql_performance 
(
  userChoice IN INT
) AS 
BEGIN 
DELETE FROM TMP_sql_performance;

INSERT INTO TMP_sql_performance
(sql_text,
sql_fulltext,
cpu_time,
last_load_time)

   (SELECT sql_text, sql_fulltext, cpu_time,
last_load_time from v$sql 
WHERE (CASE 
        WHEN userChoice = 3 AND TO_DATE(substr(last_load_time, 1, 10),'YYYY-MM-DD') > ADD_MONTHS(TRUNC(SYSDATE), -12) THEN 1    --One Year
        WHEN userChoice = 2 AND TO_DATE(substr(last_load_time, 1, 10),'YYYY-MM-DD') > ADD_MONTHS(TRUNC(SYSDATE), -1)  THEN 1    --One Month
        WHEN userChoice = 1 AND TO_DATE(substr(last_load_time, 1, 10),'YYYY-MM-DD') > TRUNC(SYSDATE -7) THEN 1                  --One Week
        WHEN userChoice = 0 AND TO_DATE(substr(last_load_time, 1, 10),'YYYY-MM-DD') > TRUNC(SYSDATE -1) THEN 1                  --one Day
      ELSE 0
    END) = 1);

select * from tmp_sql_performance;

END BUILDREPORT_sql_performance;

And it gives me: Error(24,3): PLS-00428: an INTO clause is expected in this SELECT statement. Probably I can query only once or twice in the procedure

Trex87
  • 15
  • 5
  • 1
    No, I think the issue is your syntax. It looks like you're missing an INTO in your SELECT statement. I've never heard of a stored proc being limited by how many queries can be performed inside of it. – Michael Armes Oct 25 '16 at 18:19
  • 1
    [There are some limits](https://docs.oracle.com/cloud/latest/db112/LNPLS/limits.htm), but that isn't one of them. Not sure how you jumped to that conclusion from the (fairly clear, for Oracle) error. [Read more about static SQL statements in PL/SQL blocks](https://docs.oracle.com/cloud/latest/db112/LNPLS/static.htm#LNPLS99878). – Alex Poole Oct 25 '16 at 18:24
  • What if I want just to output results with last select statement, why should I use INTO? – Trex87 Oct 25 '16 at 18:29
  • You have to return a cursor to return a record set from a stored procedure. [Check out this question for an example](http://stackoverflow.com/questions/27704675/oracle-stored-procedure-return-recordset-with-field-names) – JNevill Oct 25 '16 at 18:30
  • Output where? Who/what will call your procedure, and how will they view or consume the result of that query? You might want to add an OUT parameter (of type `sys_refcursor`), so the caller can see what you selected, [like this](http://stackoverflow.com/a/4305776/266304). You have to modify the call to the procedure to match. You can test it [like this](http://stackoverflow.com/a/8618084/266304), at least from SQL\*Plus or SQL Developer. – Alex Poole Oct 25 '16 at 18:31
  • thank you for your feedback guys, but can you provide some piece of code as an example of solution, or it's too much to ask? – Trex87 Oct 25 '16 at 18:44
  • I've linked to two bits of code. You haven't said what your requirement is though. – Alex Poole Oct 25 '16 at 18:45
  • my requirements: whenever I execute that procedure, it must show me the updated table. Thank you for your code examples – Trex87 Oct 25 '16 at 18:48
  • 1
    Are you coming to Oracle from some other database engine like SQL Server? Alex showed you how to modify the code so that is has an `OUT` parameter. But even then, the structure is odd. There is no benefit to having a table named `tmp_sql_performance`, for example. If you want to open the cursor, just query `v$sql` directly. If `tmp_sql_performance` is a global temporary table or, worse, a permanent table, and this is being called from an application, there may be significant but subtle bugs if you expect this to behave like a SQL Server temporary table. – Justin Cave Oct 25 '16 at 19:03
  • Ha, I didn't even look at what the procedure was doing! – Alex Poole Oct 25 '16 at 19:12

1 Answers1

0

You have to select into something, which has to be a variable, record or collection (depending on the data you're getting - number of columns and rows). Or you can have a cursor that you iterate over and do something with (including possibly, for debugging purposes only, logging with dbms_output).

It sounds like you want to return the query result to the procedure caller, which you can do with a ref cursor OUT parameter:

create or replace PROCEDURE BUILDREPORT_sql_performance 
(
  userChoice IN INT,
  resultCursor OUT SYS_REFCURSOR
) AS 
BEGIN 
DELETE FROM TMP_sql_performance;

INSERT INTO TMP_sql_performance
(sql_text,
sql_fulltext,
cpu_time,
last_load_time)

   (SELECT sql_text, sql_fulltext, cpu_time,
last_load_time from v$sql 
WHERE (CASE 
        WHEN userChoice = 3 AND TO_DATE(substr(last_load_time, 1, 10),'YYYY-MM-DD') > ADD_MONTHS(TRUNC(SYSDATE), -12) THEN 1    --One Year
        WHEN userChoice = 2 AND TO_DATE(substr(last_load_time, 1, 10),'YYYY-MM-DD') > ADD_MONTHS(TRUNC(SYSDATE), -1)  THEN 1    --One Month
        WHEN userChoice = 1 AND TO_DATE(substr(last_load_time, 1, 10),'YYYY-MM-DD') > TRUNC(SYSDATE -7) THEN 1                  --One Week
        WHEN userChoice = 0 AND TO_DATE(substr(last_load_time, 1, 10),'YYYY-MM-DD') > TRUNC(SYSDATE -1) THEN 1                  --one Day
      ELSE 0
    END) = 1);

OPEN resultCursor FOR
  select * from tmp_sql_performance;

END BUILDREPORT_sql_performance;
/

The call to the procedure then needs to pass a sys_refcursor variable, and do something with the resulting cursor.

For testing purposes you can use an anonymous block, and - at leats in SQL*Plus and SQL Developer - a client-defined bind variable:

variable rc refcursor

exec buildreport_sql_performance(42, :rc);

print rc

The exec is a shorthand for an anonymous block, so it's the same as doing:

begin
  buildreport_sql_performance(42, :rc);
end;
/

Notice the colon before :rc in the procedure call. That indicated you're passing a bind variable that is defined outside the PL/SQL block.

The variable and print commands are Oracle client commands, not SQL. Other client might not recognise them, though some mimic the Oracle behaviour.


You said "whenever I execute that procedure, it must show me the updated table". That is up to whoever or whatever executes it. You can't display anything directly from PL/SQL - it isn't really designed as an interactive language. You may end up calling this from, say, a Java program via JDBC, and will then have Java code that iterates over the result set and does something with the values in each row. Or it may be called from other PL/SQL, or a scheduled job, or somewhere else there is no logical place for output to be displayed. In the example above the client program is calling the procedure and displaying the OUT cursor, but only if explicitly told to.


As Justin Cave pointed out, what you're doing looks like an attempt to mimic behaviour in another database system. It isn't obvious that you really need a (possibly global temporary) table; you can query v$sql directly when you open the cursor:

create or replace procedure buildreport_sql_performance 
(
  userchoice in int,
  resultcursor out sys_refcursor
) as 
begin 
  open resultcursor for
   select sql_text, sql_fulltext, cpu_time, last_load_time from v$sql 
where (case 
        when userchoice = 3 and to_date(substr(last_load_time, 1, 10),'YYYY-MM-DD') > add_months(trunc(sysdate), -12) then 1    --One Year
        when userchoice = 2 and to_date(substr(last_load_time, 1, 10),'YYYY-MM-DD') > add_months(trunc(sysdate), -1)  then 1    --One Month
        when userchoice = 1 and to_date(substr(last_load_time, 1, 10),'YYYY-MM-DD') > trunc(sysdate -7) then 1                  --One Week
        when userchoice = 0 and to_date(substr(last_load_time, 1, 10),'YYYY-MM-DD') > trunc(sysdate -1) then 1                  --one Day
      else 0
    end) = 1;
end buildreport_sql_performance;
/

Or without duplicating the to_date() call:

create or replace procedure buildreport_sql_performance 
(
  userchoice in int,
  resultcursor out sys_refcursor
) as 
begin 
  open resultcursor for
    select sql_text, sql_fulltext, cpu_time, last_load_time from v$sql 
    where to_date(substr(last_load_time, 1, 10),'YYYY-MM-DD') > case
        when userchoice = 3 then add_months(trunc(sysdate), -12)   --One Year
        when userchoice = 2 then add_months(trunc(sysdate), -1)    --One Month
        when userchoice = 1 then trunc(sysdate -7)                 --One Week
        when userchoice = 0 then trunc(sysdate -1)                 --one Day
        else null
    end;
end buildreport_sql_performance;
/
Alex Poole
  • 183,384
  • 11
  • 179
  • 318