PL/SQL Developer - Creating dynamic SQL - sql

I'm using PL/SQL Developer. I'm trying to get query results to excel via vba. Since query is so long, i decided to create table with the query results and then simply get the table results with vba. In order to create table via excel i needed to create procedure with dynamic sql. So this is what i tried so far (even this simple example doesn't work):
create or replace procedure d_x IS
str VARCHAR(81) = 'create table as select 1 as x from dual'
BEGIN
EXECUTE IMMEDIATE str;
END;
Procedure completes without error. But when i try to execute it to create table it throws an error.
Execute statement:
EXECUTE d_x;
The execute statement throws 'ORA-00900' Invalid sql statement error.
I'm kinda new to pl sql so i couldn't find a solution to this.
Any help would be appreciated, thanks.

Procedure you posted can't possibly execute without errors because it is invalid. When fixed, looks like this:
SQL> create or replace procedure d_x IS
2 str VARCHAR(81) := 'create table test as select 1 as x from dual';
3 BEGIN
4 EXECUTE IMMEDIATE str;
5 END;
6 /
Procedure created.
In tools that support execute, you can run it as:
SQL> execute d_x
PL/SQL procedure successfully completed.
SQL> select * from test;
X
----------
1
"Correct" way - which works anywhere - is to enclose it (the procedure) into begin-end block:
SQL> drop table test;
Table dropped.
SQL> begin
2 d_x;
3 end;
4 /
PL/SQL procedure successfully completed.
SQL>
I suggest you do that.

In PL/SQL Developer you can click on the procedure name and choose 'Test', which will generate a calling script as a Test window.
You can only use execute in a Command window, because it's a SQL*Plus command and not part of SQL or PL/SQL, and the Command window is a SQL*Plus emulator.
In a SQL window you could either use a complete PL/SQL block:
begin
d_x;
end
/
or use the SQL call command:
call d_x();
Note that call() requires brackets regardless of whether or not the procedure has any parameters.
The / character is useful as a separator when using PLSQL blocks in a SQL window where you have more than one statement, otherwise PL/SQ Developer won't know where one ends and the next starts.
A Test window can only have one statement and only a single PL/SQL block or SQL statement is allowed, so there is no / character at the end.

Related

Oracle: Setting Session Parameters in Packages and Procedures

I'm a SQL Server DBA currently getting up to speed on Oracle. I'm trying to create something very similar to sp_WhoIsActive for SQL Server but for Oracle without reinventing the wheel. Essentially all I'm doing is selecting some values from v$session and inserting them into a table (poor man's ASH/AWR).
It would seem that in Oracle 12.1, there's a bug when querying dictionary views where it can take forever due to bad parsing logic (Bug 22225899 : SLOW PARSE FOR COMPLEX QUERY). The work-around is to set a session parameter:
alter session set "_optimizer_squ_bottomup"=false;
In T-SQL, I could very easily execute a stored procedure in-session and set this variable at runtime. However in Oracle, it wouldn't seem thats the case.
Sample Code:
CREATE OR REPLACE PROCEDURE SP_DB_ACTIVITY
(
v_temp NUMBER :=1
) IS
BEGIN
alter session set "_optimizer_squ_bottomup"=false;
INSERT INTO SY_DB_ACTIVITY
SELECT
<fields>
FROM
v$session;
commit;
When I run this, I get the error:
"PLS-00103: Encountered symbol 'ALTER' when expecting one of the following..."
Right now, the only way I know how to do this is via a utility like SQL Plus that initiates an interactive user session. Can anyone give me some direction as to how Oracle handles this situation? I'd like to bundle this up into a SP or a Package and then call it from Oracle Scheduler.
Hre is a simple example how to execute alter session inside of the procedure:
CREATE PROCEDURE SP_DB_ACTIVITY IS
BEGIN
EXECUTE IMMEDIATE 'alter session set "_optimizer_squ_bottomup"=false';
END;
/
Here is the way you can combine that with your select and insert statement:
CREATE OR REPLACE PROCEDURE SP_DB_ACTIVITY
(v_temp IN number) AS
v_Id NUMBER;
BEGIN
EXECUTE IMMEDIATE 'alter session set "_optimizer_squ_bottomup"=false';
SELECT 1
INTO v_Id
FROM dual;
INSERT INTO SY_DB_ACTIVITY (id) VALUES(v_Id);
END SP_DB_ACTIVITY;
/
Here is a small DEMO where you can see what will procedure do when you call it and how you can call it. Also, in this example you are calling procedure with and IN parameter. So you can use that parameter for something and in the example above is the procedure without any parameters...
You can also, of course, insert into table directly:
CREATE OR REPLACE PROCEDURE SP_DB_ACTIVITY
(v_temp IN number) AS
v_Id NUMBER;
BEGIN
EXECUTE IMMEDIATE 'alter session set "_optimizer_squ_bottomup"=false';
INSERT INTO SY_DB_ACTIVITY(id)
select 1
from dual;
END SP_DB_ACTIVITY;
/

How to execute stored procedures containing dynamic SQL in oracle?

I've created the following procedure
Create or replace procedure abcd
(
tab_name in USER_TABLES.table_name%type
)
is
begin
execute immediate
'select * from'||tab_name;
end abcd;
The procedure gets compiled.
I am trying to get the output using the following
select abcd('Table') from dual ;
I am new to dynamic SQL and this does not seem to work for me. I keep getting the error
[Error] Execution (44: 8): ORA-00904: "ABCD": invalid identifier
Can someone please help ?
Regards,
Kshitij
You're missing a space before your table name:
create or replace procedure abcd (tab_name in USER_TABLES.table_name%type )
is
begin
execute immediate 'select * from '||tab_name;
end abcd;
This won't work because you're trying to call it as a function, not a procedure:
select abcd('Table') from dual ;
Your second attempt should now work:
exec abcd('Table');
... but will now get a different error. In PL/SQL you have to select into something. In this case you probably want to open a cursor with the dynamic string and do something with the results. Not really sure what your end goal is though.
You should also read up about SQL injection while you learn about dynamic SQL.
you cannot perform a select on a procedure, a function will work only if single record return.
use
begin
abcd();
end;
or use
execute keyword
ALSO use a space after from in query
It will not work.
When you invoke EXECUTE IMMEDIATE the sql statement is send to SQL engine. No results are passed back to the PL/SQL.
Writing "SELECT * FROM a_table" is not that hard and much safer.

Solution to "cannot perform a DML operation inside a query"?

I am using a Data Analysis tool and the requirement I have was to accept a value from the user, pass that as a parameter and store it in a table. Pretty straighforward so I sat to write this
create or replace
procedure complex(datainput in VARCHAR2)
is
begin
insert into dumtab values (datainput);
end complex;
I executed this in SQL Developer using the following statement
begin
complex('SomeValue');
end;
It worked fine, and the value was inserted into the table. However, the above statements are not supported in the Data Analysis tool, so I resorted to use a function instead. The following is the code of the function, it compiles.
create or replace
function supercomplex(datainput in VARCHAR2)
return varchar2
is
begin
insert into dumtab values (datainput);
return 'done';
end supercomplex;
Once again I tried executing it in SQL Developer, but I got cannot perform a DML operation inside a query upon executing the following code
select supercomplex('somevalue') from dual;
My question is
- I need a statement that can run the mentioned function in SQL Developer or
- A function that can perform what I am looking for which can be executed by the select statement.
- If it is not possible to do what I'm asking, I would like a reason so I can inform my manager as I am very new (like a week old?) to PL/SQL so I am not aware of the rules and syntaxes.
P.S. How I wish this was C++ or even Java :(
EDIT
I need to run the function on SQL Developer because before running it in DMine (which is the tool) in order to test if it is valid or not. Anything invalid in SQL is also invalid in DMine, but not the other way around.
Thanks for the help, I understood the situation and as to why it is illegal/not recommended
You could use the directive pragma autonomous_transaction. This will run the function into an independant transaction that will be able to perform DML without raising the ORA-14551.
Be aware that since the autonomous transaction is independent, the results of the DML will be commited outside of the scope of the parent transaction. In most cases that would not be an acceptable workaround.
SQL> CREATE OR REPLACE FUNCTION supercomplex(datainput IN VARCHAR2)
2 RETURN VARCHAR2 IS
3 PRAGMA AUTONOMOUS_TRANSACTION;
4 BEGIN
5 INSERT INTO dumtab VALUES (datainput);
6 COMMIT;
7 RETURN 'done';
8 END supercomplex;
9 /
Function created
SQL> SELECT supercomplex('somevalue') FROM dual;
SUPERCOMPLEX('SOMEVALUE')
--------------------------------------------------------------------------------
done
SQL> select * from dumtab;
A
--------------------------------------------------------------------------------
somevalue
Tom Kyte has a nice explanation about why the error is raised in the first place. It is not safe because it may depend upon the order in which the rows are processed. Furthermore, Oracle doesn't guarantee that the function will be executed at least once and at most once per row.
Just declare a variable to accept the return value, for example:
declare
retvar varchar2(4);
begin
retvar := supercomplex('somevalue');
end;
The select doesn't work because the function is performing an insert, if all it did was return a value then it would work.
Just execute the function in a dummy if ... end if; statement to ignore the return value:
exec if supercomplex('somevalue') then null; end if;
Or execute it as a parameter for put_line procedure to output the return value:
exec dbms_ouput ('result of supercomplex='||supercomplex('somevalue'));
result of supercomplex=done

Declaring & Setting Variables in a Select Statement

I'm attempting to write a simple query where I declare some variables and then use them in a select statement in Oracle. I've been able to do this before in SQL Server with the following:
DECLARE #date1 DATETIME
SET #date1 = '03-AUG-2010'
SELECT U.VisualID
FROM Usage u WITH(NOLOCK)
WHERE U.UseTime > #Date1
From the searching I've done it appears you can not declare and set variables like this in Select statements. Is this right or am I mssing something?
From the searching I've done it appears you can not declare and set variables like this in Select statements. Is this right or am I missing something?
Within Oracle PL/SQL and SQL are two separate languages with two separate engines. You can embed SQL DML within PL/SQL, and that will get you variables. Such as the following anonymous PL/SQL block. Note the / at the end is not part of PL/SQL, but tells SQL*Plus to send the preceding block.
declare
v_Date1 date := to_date('03-AUG-2010', 'DD-Mon-YYYY');
v_Count number;
begin
select count(*) into v_Count
from Usage
where UseTime > v_Date1;
dbms_output.put_line(v_Count);
end;
/
The problem is that a block that is equivalent to your T-SQL code will not work:
SQL> declare
2 v_Date1 date := to_date('03-AUG-2010', 'DD-Mon-YYYY');
3 begin
4 select VisualId
5 from Usage
6 where UseTime > v_Date1;
7 end;
8 /
select VisualId
*
ERROR at line 4:
ORA-06550: line 4, column 5:
PLS-00428: an INTO clause is expected in this SELECT statement
To pass the results of a query out of an PL/SQL, either an anonymous block, stored procedure or stored function, a cursor must be declared, opened and then returned to the calling program. (Beyond the scope of answering this question. EDIT: see Get resultset from oracle stored procedure)
The client tool that connects to the database may have it's own bind variables. In SQL*Plus:
SQL> -- SQL*Plus does not all date type in this context
SQL> -- So using varchar2 to hold text
SQL> variable v_Date1 varchar2(20)
SQL>
SQL> -- use PL/SQL to set the value of the bind variable
SQL> exec :v_Date1 := '02-Aug-2010';
PL/SQL procedure successfully completed.
SQL> -- Converting to a date, since the variable is not yet a date.
SQL> -- Note the use of colon, this tells SQL*Plus that v_Date1
SQL> -- is a bind variable.
SQL> select VisualId
2 from Usage
3 where UseTime > to_char(:v_Date1, 'DD-Mon-YYYY');
no rows selected
Note the above is in SQLPlus, may not (probably won't) work in Toad PL/SQL developer, etc. The lines starting with variable and exec are SQLPlus commands. They are not SQL or PL/SQL commands. No rows selected because the table is empty.
I have tried this and it worked:
define PROPp_START_DT = TO_DATE('01-SEP-1999')
select * from proposal where prop_start_dt = &PROPp_START_DT
The SET command is TSQL specific - here's the PLSQL equivalent to what you posted:
v_date1 DATE := TO_DATE('03-AUG-2010', 'DD-MON-YYYY');
SELECT u.visualid
FROM USAGE u
WHERE u.usetime > v_date1;
There's also no need for prefixing variables with "#"; I tend to prefix variables with "v_" to distinguish between variables & columns/etc.
See this thread about the Oracle equivalent of NOLOCK...
Try the to_date function.
Coming from SQL Server as well, and this really bugged me. For those using Toad Data Point or Toad for Oracle, it's extremely simple. Just putting a colon in front of your variable name will prompt Toad to open a dialog where you enter the value on execute.
SELECT * FROM some_table WHERE some_column = :var_name;

Dynamic SQL not working as expected

create or replace procedure createtables
Authid current_user as
begin
execute immediate 'create table newcustomer as select * from customer';
end;
create or replace procedure e
is
begin
createtables;
select * from newcustomer;
end;
I got two procedures above. first one will create a new tables called newcustomer, second procedure will call the first procedure and query to the newcustomer table. when I try to compile this code, it says the table is not yet created, I don't really get it as I have called createtables procedure so I assume I have created the table.
Any help will be appreciated. Thanks
Compiling the second procedure without executing the first procedure first will fail, since the table has not been created.
You cannot compile a procedure that relies on objects that do not exist.
Use EXEC createtables before creating procedure e, and do not call createtables in there.
Procedure e will also not compile because you are not using the results of select * from newcustomer as cursor or store the results into variables.
EDIT:
Instead of procedures, you could use an anonymous block. Put the following into a file and execute it (via SQL*Plus for example):
Create Table newcustomer As Select * From customer;
Begin
Null; --# Do something with your new table in here.
End;
/