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

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

Related

Using Oracle bind variables in an application which only supports standard SQL

I have an application which works fine to execute and parse standard SQL queries, but not PL/SQL.
I'm trying to use bind variables, and because of lack of PL/SQL support I thought I could create a function that accepts a single variable, queries a DB, and returns the result.
My function is as follows:
CREATE OR REPLACE FUNCTION BIND_FUNC
(
IN_FLOAT IN BINDING_TEST.C2_FLOAT%TYPE
) RETURN SYS_REFCURSOR AS
BINDTEST_CURSOR SYS_REFCURSOR;
BEGIN
OPEN BINDTEST_CURSOR FOR
'SELECT * FROM BINDING_TEST WHERE C2_FLOAT = :IN_FLOAT' USING IN_FLOAT;
RETURN BINDTEST_CURSOR;
END BIND_FUNC;
My problem is that I'm not sure how to make the return value appear to my application as a normal result set.
In SQL Developer if I run the query SELECT BINDING_FUNC(1) FROM BINDING_TEST I get a result like:
{<C1_VARCHAR2=This is some text,C2_FLOAT=1>,}
If I do something like below I get more along the results I'm looking for, but again the syntax doesn't appear supported in my application:
VARIABLE cur REFCURSOR;
BEGIN
:cur := BIND_FUNC(1);
END;
/
PRINT cur;

Test if anything was updated in dynamic query

I need to construct and execute an UPDATE statement dynamically. Then I need to test whether anything was updated at all. My code is as follows:
DECLARE
v_table_name text;
v_column_name text;
v_debug_flag boolean;
v_upd_stmt text;
BEGIN
select nm_table_name, replace(nm_table_name, 'CDB_', 'ID_')
into strict v_table_name, v_column_name
from m_entity e
where id=12;
v_upd_stmt := format('update %s set id_lock_usr =null, dt_lock=null where %s=$1 returning id_lock_usr',
v_table_name,
v_column_name);
execute v_upd_stmt using p_id;
END
How to know if anything was updated?
How to know if anything was updated ?
Various options. In a plpgsql function, you can check the special variable FOUND to see if the last SQL command affected any rows.
IF FOUND THEN ...
However, For dynamic queries with EXECUTE use GET DIAGNOSTICS instead. The manual:
Note in particular that EXECUTE changes the output of GET DIAGNOSTICS,
but does not change FOUND.
Related:
Dynamic SQL (EXECUTE) as condition for IF statement
Aside:
I see lingering problems with properly escaped identifiers (especially with upper case table names like you have), possibly even SQL injection. Fix with:
DECLARE
v_table_name text;
v_column_name text;
v_id_lock_usr integer; -- guessing the data type
i integer;
BEGIN
SELECT nm_table_name, replace(nm_table_name, 'CDB_', 'ID_')
INTO strict v_table_name, v_column_name
FROM m_entity e
WHERE id = 12;
EXECUTE format('UPDATE %I SET id_lock_usr = null, dt_lock = null
WHERE %I = $1 RETURNING id_lock_usr'
, v_table_name,
, v_column_name)
INTO v_id_lock_usr; -- to store the *single* result from RETURNING
GET DIAGNOSTICS i = ROW_COUNT;
IF i > 0 THEN
-- do something
END IF;
END
Note %I instead of %s.
In your case, if id_lock_usr returned by your query is NOT NULL (reliably), you might just test the result directly instead:
IF v_id_lock_usr IS NOT NULL ...
And you might want to schema-qualify table names to avoid ambiguities. But you must escape schema_name.table_name as two separate identifiers ..
Related:
Are PostgreSQL column names case-sensitive?
Define table and column names as arguments in a plpgsql function?
Table name as a PostgreSQL function parameter

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...

Does Oracle close the SYS_REFCURSOR returned from a function which is used in a SELECT statement?

I need to store some queries in the DB (inside functions in packages) and then call the functions from SQL Developer (from ORDS), so I found that you can return the queries in SYS_REFCURSORs from the stored functions like this:
CREATE OR REPLACE FUNCTION test RETURN SYS_REFCURSOR AS
vRC SYS_REFCURSOR;
BEGIN
OPEN vRC FOR SELECT *
FROM employees
WHERE empid = 34650;
RETURN vRC;
END;
And later on, simply retrieve the data in SQL Developer like this:
SELECT test
FROM dual;
So my question is...
Is this correct? I know that everytime we OPEN a cursor we need to explicitaly CLOSE it and every example of this that I have foud close the refcursor in PL/SQL and I need to get the data from a SELECT statement (in order to parse it to JSON in SQL DEVELOPER).
Also I have found that in SQLPlus the "print" statement closes the cursor once it fetched all the data, does the SELECT statement im my example do this as well?
You can get the cursor and print its contents like this:
VARIABLE cur REFCURSOR;
BEGIN
:cur := test();
END;
/
PRINT cur;
The PRINT and the SELECT statements will both read the cursor and when they have read all the rows they will implicitly close the cursor. You cannot read the data from a cursor twice (however, you can call the function multiple times to get multiple cursors all containing the same information).

variable as column name in where clause in Oracle PL/SQL

I am working on PL/SQL code where I need to perform a select query using variable as column name in where clause. Column names are stored in a table as varchar and I am using a loop to pass those column names to my select statement.
Please find sample code segment I am trying to run:
set serveroutput on;
declare
var varchar2(100);
counter number;
begin
var:='description';
select count(*)
into counter
from nodetable
where var like '%Ship%';
dbms_output.put_line(counter);
end;
Output:
anonymous block completed
0
However the result should be 86.
Oracle is comparing last condition as two string and not column=string.
Please let me know if this is even feasible in oracle or if there is a workaround for it.
Regards
Ankit
You have to use dynamic SQL, preferrably with bind-variables:
EXECUTE IMMEDIATE
'select count(*) from nodetable where '||var||' like :p1'
INTO counter
USING '%Ship%';
Try this
declare
var varchar2(100);
counter number;
begin
var:='description';
EXECUTE IMMEDIATE
'select count(*)
into counter
from nodetable
where '||var||' like ''%Ship%'' ';
dbms_output.put_line(counter);
end;
You need to be carefull with the colon's (').
I agreed with previous answer in implementation, but i strictly recommend you to change your technical requirements, because you can't use bind variables for this, and it's potential place for injection. For example, if someone will edit value in your table which stores column names, to something like that: "description = inject_function or description". Then your dynamic sql block will execute this statement:
select count(*) from nodetable where description = inject_function or description like '%Ship%
and example implementation of function
create function inject_function
return varchar2
is pragma autonomous_transaction;
begin
delete * from most_important_table;
commit;
return to_char(null);
exception when others then
rollback;
return to_char(null);
end;