Dynamic SQL with variable amount of parameters - sql

I know the following works (this would be part of the code inside a stored procedure):
sql_string := 'INSERT INTO my_table (data_id, data_col) SELECT SYS_GUID(),''{"json_id1":'' || (:value_1 - val_from_view_1 + :i - 1) || ''}'' FROM my_view WHERE col_1 <= :value_2';
FOR i IN 1..N LOOP
EXECUTE IMMEDIATE sql_string
USING IN OUT input_value_1, i, input_value_2;
END LOOP;
My question is, since that sql_string comes as an input parameter to my stored procedure, how do I deal with a variable number of parameters for the USING clause? That is, how do I cover the above case, a case where the sql_string is such that I have something like this:
EXECUTE IMMEDIATE sql_string
USING IN OUT input_value_1, i, input_value_2, ..., input_value_N;
and everything in between?
PS: I'm not married to EXECUTE IMMEDIATE, so if there's a better way to do this, feel free to suggest it.

Related

Dynamic query building yields no results and crashes process

I am trying to run a SQL query within a PL/SQL script that takes dynamic parameters and should return a specific result. For that I tried the EXECUTE IMMEDIATE command. Unfortunately it crashes always my variable is a varchar. If it is a number instead it runs perfetcly.
Here is my tried code:
plsql_request := 'SELECT :x FROM TEST_TABLE WHERE ID = :y';
EXECUTE IMMEDIATE plsql_request INTO plsql_result USING variable_1, '2837123 | hsiae';
While testing it crashes always in this constelation.
Second approch:
plsql_request := 'SELECT ' || variable_1 || ' FROM TEST_TABLE WHERE ID = ''' || variable_2 || '''';
EXECUTE IMMEDIATE plsql_request ;
I do not get any results at all, hence I think the query-building using dynamic variables in combination with varchars fails.
Any help apreciated,
Filip.
You cannot use a bind variable for an object literal like a column name, table name, etc.. so you must use the second method with || variable_1 || to splice in your dynamic column name. However, you should use real bind variables :y, USING, etc.. for your predicate value in order to not generate an excessive number of child cursors.
So, :
plsql_request := 'SELECT '||variable_1||' FROM TEST_TABLE WHERE ID = :y';
EXECUTE IMMEDIATE plsql_request INTO plsql_result USING '2837123 | hsiae';

Teradata stored procedure with dynamic parameters called from R script

I need to extract some data from Teradata to process in R. I have around 84 Dep/sec keys with most of them having a different time span so my thought was to create a stored procedure in Teradata that will accept the Dep, Sec and Dates as parameters. I could then loop over the list in R calling the SP each time to create my data set.
The SP I have created to test this idea is a very simple one but I can't get it to work.
CREATE PROCEDURE procTest4 (IntN integer)
BEGIN
CALL DBC.SysExecSQL('SELECT top' || IntN || '*
from TableName');
END;
Teradata does create the SP but I don't know how to execute it and pass the paramters to it. When I try:
Call procText4(10)
I get the following error:
5568: SQL statement is not supported within a stored procedure.
The only other option for me is to create the SQL string in R and then run it from there but there is multiple passes of SQL which create volatile tables and the RODBC package doesn't seem to like them, plus it's a very messy way of doing it.
Any help is much appreciated.
The syntax for returning a result set from a Stored Procedure using Dynamic SQL is a bit complex:
CREATE PROCEDURE procTest4 (IntN INTEGER)
DYNAMIC RESULT SETS 1
BEGIN
DECLARE SqlStr VARCHAR(1000);
DECLARE rslt CURSOR WITH RETURN ONLY FOR stmt;
SET SQLStr = 'SELECT top ' || IntN || ' * from TableName';
PREPARE stmt FROM SqlStr;
OPEN rslt;
END;
But you should double check if you can rewrite those loops...

SQL - Oracle Functions using variables for schema names

I am writing some oracle stored procedures where we have conditional logic which effects which schema we are working from and I am not sure how to do this in the sql for the stored proc. If I am working with prepared statements then its fine but in the scenario where I am just executing a query to say populate another variable then I dont know how to do this. For example
PROCEDURE register (
incustomer_ref in VARCHAR2,
incustomer_type in VARCHAR2,
outreturn_code out VARCHAR2
)
IS
customer_schema varchar2(30);
record_exists number(1);
BEGIN
if incustomer_type='a' then
customer_schema:='schemaA';
elsif incustomer_type='b' then
customer_schema:='schemaB';
end if;
--This type of command I cant get to work
select count(*) into record_exists from **customer_schema**.customer_registration where customer_ref=incustomer_ref
--but a statement like this i know how to do
if record_exists = 0 then
execute immediate 'insert into '||customer_schema||'.customer_registration
values ('||incustomer_ref||','Y',sysdate)';
end if;
Can anyone shine some light on what I am missing here.
Cheers
you can use execute immediate also for select statment:
execute immediate 'select count(*) from '|| customer_schema
|| '.customer_registration where customer_ref= :b1'
into record_exists using incustomer_ref;

Oracle Native Dynamic SQL PL/SQL statement without begin and end

I have the problem of creating a backup for a table by creating a series of insert statements.
The input is the table name and each table can have a different number of columns. It is assumed that data types can be varchar2, number, or date only
so I have this line of code:
execute immediate fetchStmt;
where fetchStmt can be:
fetch tableColCursor into valuesArray(1), valuesArray(2), ..., valuesArray(n)
This just fetches each row from the cursor and puts it into a varray, the statements itself works if it is not in an execute immediate statement.
I do know that an execute immediate can only process SQL queries or PL/SQL blocks.
The problems is how would I be able to make this work or what can be a similar solution to the problem?
Note that it is not known during compile time the table and its columns and their data types
EXECUTE IMMEDIATE can only process full statements, i-e: SQL statements or PLSQL blocks (with [DECLARE]..BEGIN..END).
Furthermore, a block executed this way won't see any variables from the calling block (they don't share the same scope), for instance this won't work:
DECLARE
l NUMBER := 1;
k NUMBER := 0;
BEGIN
EXECUTE IMMEDIATE 'BEGIN l := k; END;';
END;
The above code will produce an error because l and k are not defined in the sub-block. Instead you would need to use input/output variables:
DECLARE
l NUMBER := 1;
k NUMBER := 0;
BEGIN
EXECUTE IMMEDIATE 'BEGIN :P1 := :P2; END;' USING OUT l, k;
dbms_output.put_line(l); -- return 0
END;
In your case you don't know the number of variables so you won't be able to use EXECUTE IMMEDIATE. You could use DBMS_SQL but I think there are simpler methods: you could find already working code (it exists within Oracle APEX for instance), or you could program it yourself by running a SELECT that would produce the necessary INSERT string. For this method you would have to generate the SQL statement dynamically (dependent upon the table columns). The statement would look like this (for a table TEST(a,b,c) where all columns are integer [needs adapting for other datatypes]):
SELECT 'INSERT INTO test(a,b,c) VALUES ('||a||', '||b||', '||c||');'
FROM test

how to use a procedure parameter in a query

how do i access the procedure parameters inside the same procedure using a query
for example: see this procedure
procedure game(left in tab.left%type,right in tab.right%type,...)
is
--some local variables
begin
merge into tgt_table
using subquery --(here is what i need to use the parameters)
on some condition
when matched then
update the tgt table
when not matched then
insert the table;
end game;
In above procedure and in merge statement, i need a query such that it uses the parameters value as a table reference and using those values it either updates or inserts into the table based on the condition given.
Help me please. Thanks in advance
You would need to use dynamic SQL if your parameters define the table to use - something like:
procedure game(left in tab.left%type,right in tab.right%type,...)
is
--some local variables
l_sql long;
begin
l_sql := 'merge into tgt_table'
|| ' using ' || left
|| ' on some condition'
|| ' when matched then'
|| ' update ' || right
|| ' when not matched then'
|| ' insert the table';
execute immediate l_sql;
end game;
However, you have a lot more work left to do, since the condition, update and insert clauses all need to change according to the tables being used. I'm not convinced this procure will be particularly useful in fact.
Please read http://download.oracle.com/docs/cd/B14117_01/appdev.101/b10807/06_ora.htm#sthref777