pl/sql - to_date not working with execute immediate parameter - sql

i wanna be able to execute my below proc like so:
exec procname('29-JAN-2011');
proc code is:
PROCEDURE procname(pardate VARCHAR2) IS
vardate DATE := to_date(pardate, 'DD-MON-YYYY');
SQLS VARCHAR2(4000);
BEGIN
SQLS := 'SELECT cola, colb
FROM tablea
WHERE TRUNC(coldate) = TRUNC(TO_DATE('''||pardate||''',''DD/MON/YYYY''))';
EXECUTE IMMEDIATE SQLS;
END;
It keeps throwing error:
ORA-00904: "JAN": invalid identifier.
It compiles, but it throws the error when I run this command:
EXEC procname('29-JAN-2011');

You declare a variable which casts the input parameter to a date: why not use it?
Also, the TRUNC() applied to a date removes the time element. You don't need it here because the value you're passing has no time.
So, your code should be:
PROCEDURE procname(pardate VARCHAR2) IS
vardate DATE := to_date(pardate, 'DD-MON-YYYY');
SQLS VARCHAR2(4000) := 'select cola, colb FROM tablea
WHERE TRUNC(coldate) = :1';
l_a tablea.cola%type;
l_b tablea.colb%type;
BEGIN
EXECUTE IMMEDIATE SQLS
into l_a, l_b
using vardate;
END;
Specifying the dynamic SQL statement with a bind variable and executing it with the USING syntax is a lot more efficient. Note that we still have to SELECT into some variables.

You're using two different notations in the two calls to to_date. I think one of them (the second) is wrong.

Related

Pass SQL string to oracle stored procedure and get results with execute immediate

I am trying to pass in a SQL string to a stored procedure and using EXECUTE IMMEDIATE to return the results. Something like this:
CREATE PROCEDURE P360_RCT_COUNT (sqlString IN VARCHAR2)
AS
BEGIN
EXECUTE IMMEDIATE sqlString;
END;
/
I am not sure how to accomplish it. With the above, when I execute the SP using the command below, I get an error:
EXECUTE P360_RCT_COUNT 'SELECT COUNT(DISTINCT ENTITY_ID),ADDR_COUNTY FROM P360_V_RCT_COUNT GROUP BY ADDR_COUNTY';
The error is: ORA-06550: line 1, column 22:
PLS-00103: Encountered the symbol "SELECT COUNT(ENTITY_ID),ADDR_COUNTY
FROM P360_V_RCT_COUNT GROUP " when expecting one of the following:
:= . ( # % ; The symbol ":=" was substituted for "SELECT
COUNT(DISTINCT ENTITY_ID),ADDR_COUNTY FROM P360_V_RCT_COUNT GROUP " to
continue.
Basically I am building a SQL string in a system and need to pass it in to the SP and get the results back to the system. I am relatively new to stored procedures in Oracle.
The easiest way to work with a result set is sys_refcursor. This can be used quite easily with JDBC or ODBC.
Your procedure would look like this:
CREATE PROCEDURE P360_RCT_COUNT (
sqlString IN VARCHAR2
, p_result_set out sys_refcursor)
AS
BEGIN
open p_result_set for sqlString;
END;
/
Obviously the precise details of how you call it will vary according to your client. But in SQL*Plus it would be:
var rc refcursor
exec P360_RCT_COUNT( 'SELECT COUNT(DISTINCT ENTITY_ID),ADDR_COUNTY FROM P360_V_RCT_COUNT GROUP BY ADDR_COUNTY', :rc);
print rc
To return lists of values in a OUT parameter you need to decide the type(s) to use.
Say, for example, you have to return some varchar2 and some date lists, you could use something like this:
create or replace type tabOfVarchar2 is table of varchar2(100);
create or replace type tabOfDates is table of date;
create or replace procedure testProc(pString IN varchar2,
pOutVarchar1 OUT tabOfVarchar2,
pOutVarchar2 OUT tabOfVarchar2,
pOutVarchar3 OUT tabOfVarchar2,
pOutDates OUT tabOfDates
) is
begin
execute immediate pString
bulk collect into pOutVarchar1, pOutVarchar2, pOutVarchar3, pOutDates;
end;
This is way you can test this procedure:
declare
v1 tabOfVarchar2 ;
v2 tabOfVarchar2;
v3 tabOfVarchar2;
d1 tabOfDates ;
vSQL varchar2(100) := 'select ''a'', ''b'', ''c'', sysdate from dual';
begin
testProc(vSQL, v1, v2, v3, d1);
--
for i in v1.first .. v1.last loop
dbms_output.put_line(v1(i) || '/' || v2(i) || '/' || v3(i) || '/' || to_char(d1(i), 'dd/mm/yyyy'));
end loop;
end;
which gives:
a/b/c/14/04/2017
This only works with queries that give exactly a fixed number of columns, of known types.

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;

Set an out parameter value from a pl/sql block stored in a clob column

I have the following table and relevant column..
TABLEA
SQL_SCRIPT CLOB
Here is a procedure that executes the pl/sql block in the clob..
Create procedure (p_exit_code IN OUT NUMBER)
AS
V_sql_val sql_script%TYPE;
…
BEGIN
Select sql_script into v_sql_val from tablea;
EXECUTE IMMEDIATE sql_script;
END;
In SQL_SCRIPT I am trying to do following in the exception handler:
EXCEPTION
WHEN EXCP_STOP_PROCESS THEN
p_exit_code := 1;
END;
I need to set the value of p_exit_code parameter in procedure from the clob script. How can I do this?
If I understand correctly, you need to set the value of a variable within sql_script and return this to the calling procedure. This can be achieved using bind variables. A very simple test case is below:
declare
-- ':return_code' is the bind variable
sql_script varchar2(1000) := 'begin :return_code := 1; end;';
vn_result number;
begin
execute immediate sql_script using out vn_result;
dbms_output.put_line(vn_result);
end;

'insert into' in 'execute immediate' clause

Can you check this and tell me why I've got an error, please? How should it look? I have no idea what's wrong here. I need to create a table in a function, and in the same function insert data into this table:
create or replace
function new_tab ( pyt IN varchar2) return number
IS
a number;
b varchar2(20);
begin
a:= ROUND(dbms_random.value(1, 3));
b:='example';
-- This works perfect
execute immediate 'CREATE TABLE create_tmp_table'|| a ||'(i VARCHAR2(50))';
-- Here`s the problem
execute immediate 'insert into create_tmp_table'||a|| 'values ('|| b ||')';
exception
when others then
dbms_output.put_line('ERROR-'||SQLERRM);
return 0;
end;
My result is:
ERROR-ORA-00926: missing VALUES keyword. Process exited.
Where is the mistake?
As you are creating a dynamic insert command you have to create it as is. So you are missing the single quotes for the value that is varchar:
execute immediate
'insert into create_tmp_table'||a|| ' values ('''|| b ||''');';
^here ^and here
And a good suggestion for this type of error is to do some debug. On your case you could create a varchar2 variable and put your insert on it then you use the dbms_output.put_line to this variable. Then you will have your sql command that you can test direct on your database. Something like:
declare
sqlCommand varchar2(1000);
--define a and b
begin
--put some values in a and b
sqlCommand := 'insert into create_tmp_table'||a|| ' values ('''|| b ||''');';
dbms_output.put_line(sqlCommand);
end;
Then you will know what is wrong with the dynamic command.
execute immediate 'insert into TABLE'||a||' (COLUMN_NAME) values (:val)' using b;
This way you don't have to bother with escaping your values (SQL-injection hack) and correctly casting the type.
http://docs.oracle.com/cd/B13789_01/appdev.101/b10807/13_elems017.htm

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;