Error in Parameters of Stored Procedure when Dynamic SQL is used - sql

I am using Oracle Sql Developer to write a Stored Proc, which accepts a list of values separated by "," (dynamic sql) and use this variable in "in" clause to fire a query from a table.
But i am ending up with an error at
#p_case_nbr varchar2(100),
Error(4,5): PLS-00103: Encountered the symbol "#" when expecting one of the following: current delete exists prior
The SP used:
create or replace
procedure TEST_PROC
(
#p_case_nbr varchar2(100),
p_out_case_nbr out varchar2
)
as
cursor test_cur is
select t.COLUMN1
from test_table t
where t.column1 in (#p_case_nbr);
test_cur_line test_cur%ROWTYPE;
begin
p_out_case_nbr := null;
open test_cur;
LOOP
FETCH test_cur INTO test_cur_line;
EXIT WHEN test_cur%NOTFOUND;
p_out_case_nbr := p_out_case_nbr || ' ' || test_cur_line.COLUMN1;
dbms_output.put_line(test_cur_line.COLUMN1);
end loop;
close test_cur;
dbms_output.put_line('Final Value ' || p_out_case_nbr);
dbms_output.put_line('SP Completed Succesfully');
end;

In ORACLE no need of giving # before varibles
Just try to remove the # from #p_case_nbr where ever specified and try once again
and also no need of specifying the size of the parameter like #p_case_nbr varchar2(100)
just give #p_case_nbr varchar

Related

How to pass table name as a parameter in update procedure in Oracle?

I am new to Oracle so please sorry the question that seems to be very easy for you.
I need to get the following procedure with UPDATE query with replace function
CREATE OR REPLACE PROCEDURE proc_replace_space_1
(
p_table user_tables.table_name%TYPE,
p_search IN varchar2,
p_replace IN varchar2
)
IS
BEGIN
EXECUTE IMMEDIATE
'update ' || p_table ||
'set docnum = replace(docnum, :2, :3 )'
USING p_search, p_replace;
END;
This procedure removes all spaces.
But when I call it
BEGIN
proc_replace_space_1('cm_risk.fct_loans_temp', ' ', '');
END;
I've got the following error
SQL Error [971] [42000]: ORA-00971: missing SET keyword
ORA-06512: at "CM_RISK.PROC_REPLACE_SPACE_1", line 9
ORA-06512: at line 2
How can I modify my code to handle the problems?
Thank you.
Dynamic SQL is hard because it turns compilation errors into runtime errors. So I urge you to acquire the good habit of assembling your dynamic SQL as string variables which you can persist to a log table if you have such a thing (and if you don't it would be another good habit to acquire) or display using dbms_output.put_line.
So your procedure would look like this:
CREATE OR REPLACE PROCEDURE proc_replace_space_1
(
p_table user_tables.table_name%TYPE,
p_search IN varchar2,
p_replace IN varchar2
)
IS
l_sql varchar2(32767);
BEGIN
l_sql := 'update ' || p_table ||
'set docnum = replace(docnum, :2, :3 )';
EXECUTE IMMEDIATE
l_stmt
USING p_search, p_replace;
exception
when others then
dbms_output.put_line(l_sql);
raise;
END;
This approach allows you to see the actual SQL your procedure executed. Probably you'll be able to spot the syntax error immediately (in this case it's the missing space between table name and set). Otherwise you can try to run the statement for yourself and see what the SQL compiler highlights.
NB: depending on your environment you may need to enable DBMS_OUTPUT before you can see the message.
You just need to add a space before set. Currently your table name is appended to set keyword and it is assuming it as a table name i.e MyTableSet
CREATE OR REPLACE PROCEDURE proc_replace_space_1
(
p_table user_tables.table_name%TYPE,
p_search IN varchar2,
p_replace IN varchar2
)
IS
BEGIN
EXECUTE IMMEDIATE
'update ' || p_table ||
' set docnum = replace(docnum, :2, :3 )'
USING p_search, p_replace;
END;

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.

Executing SQL via Oracle Stored Procedure passing variables

I created the following Stored Procedure in Oracle:
create or replace
PROCEDURE APPUSERCT
(
PROJNAME IN VARCHAR2
, WHEREDATE IN VARCHAR2 ,
cnt OUT long
) AS
BEGIN
select count(Distinct UPPER(field1)) into cnt from bs_log where application_name=PROJNAME and field1 is not null and log_type='info' || WHEREDATE;
END APPUSERCT;
In my php page WHEREDATE is set as:
$whereDate=" and (TO_DATE(created_on,'mm/dd/yyyy HH24:MI:SS')>= TO_DATE('".$startDate."','mm/dd/yyyy') AND TO_DATE(created_on,'mm/dd/yyyy HH24:MI:SS')<= TO_DATE('".$endDate."','mm/dd/yyyy'))";
I then Bind the parameters and values and call the SP:
$sql = 'BEGIN APPUSERCT(:projName,:whereDate,:cnt); END;';
$result = oci_parse($dbconn, $sql);
oci_bind_by_name($result,':cnt',$totalRowCount,32);
oci_bind_by_name($result,':projName',$projName,32);
oci_bind_by_name($result,':whereDate',$whereDate,200);
oci_execute($result);
If no date range is entered on the php page by the user, then the WHEREDATE is blank in the SP and the SQL runs without any range or any additional syntax appended to the end of it in the Store Procedure (SP).
When the user enters a date range on the php page then the WHEREDATE param becomes:
and (TO_DATE(created_on,'mm/dd/yyyy HH24:MI:SS')>=TO_DATE('05/01/2015','mm/dd/yyyy') AND TO_DATE(created_on,'mm/dd/yyyy HH24:MI:SS')<=TO_DATE('05/07/2015','mm/dd/yyyy'))
and it is appended to the end of the SQL in the SP. But whenever this happens my php page always returns 0 count from the SQL executed. No error, just 0 count.
If I try running the SQL directly in Oracle it's:
select count(Distinct UPPER(field1)) as cnt from bs_log where application_name='Myweather' and field1 is not null and log_type='info' and (TO_DATE(created_on,'mm/dd/yyyy HH24:MI:SS')>= TO_DATE('05/01/2015','mm/dd/yyyy') AND TO_DATE(created_on,'mm/dd/yyyy HH24:MI:SS')<= TO_DATE('05/07/2015','mm/dd/yyyy'))
and I get results. I get a count back. But when calling it through the procedure I get 0. Can anyone see why?
Thanks!
Well, a couple of problems show up.
First, I suggest that you change the declaration of the cnt parameter from LONG to NUMBER. LONG is not a numeric type in Oracle; instead, it's a type of LOB whose use is deprecated. If you really want to return a LOB use either BLOB or CLOB, whichever is appropriate.
Second, you can't pass in a character string containing part of a WHERE clause (the WHEREDATE parameter) and concatenate it to the end of an SQL statement. In this case you were contatenating the text in WHEREDATE to the string literal info, which I suspect is not what you had in mind. You should probably be using dynamic SQL, in a manner similar to the following:
create or replace PROCEDURE APPUSERCT(PROJNAME IN VARCHAR2,
WHEREDATE IN VARCHAR2,
cnt OUT NUMBER)
AS
strSql_text VARCHAR2(32767) := 'select count(Distinct UPPER(field1)) ' ||
' from bs_log where application_name=''' || PROJNAME ||
''' and field1 is not null and ' ||
' log_type=''info'' ' || WHEREDATE;
csr SYS_REFCURSOR;
BEGIN
OPEN csr FOR strSql_text;
FETCH csr INTO cnt;
CLOSE csr;
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('APPUSERCT - exception : ' || SQLCODE || ' (' ||
SQLERRM || ')');
RAISE;
END APPUSERCT;
So you build up the SQL statement as a text string, including your addition to the WHERE clause, then use the OPEN statement to open a cursor for that statement. Then the cursor is fetched, placing the result into cnt, then the cursor is closed, and finally the routine exits. I've included a default EXCEPTION handler as well - always a good idea in any routine.

Cursor loaded with dynamic SQL

I got a problem with a cursor that I load with dynamic SQL in procedure.
My query contains a date and I have this error :
ORA-00932: inconsistent datatypes ; expected: DATE ; got: NUMBER
Here is my procedure :
create or replace procedure EMP_CURSOR (
p_date in date,
p_schema in varchar2
) is
c_emp sys_refcursor;
begin
open c_emp for
'select ID, NAME
from ' || DBMS_ASSERT.schema_name(p_schema) || '.EMP
where DATE_MAJ >= ' || p_date;
EMP (c_emp);
exception
when others then
DBMS_OUTPUT.put_line(SQLERRM);
end;
And this is how I call it :
exec EMP_CURSOR(to_date('01/01/2015','dd/MM/yyyy'),'TEST');
I don't know how to pass a date for a dynamic query.
When I removed the dynamic part and I put the schema name in the query, it works fine.
Oracle is implicitly converting p_date to a string according to your NLS_DATE_FORMAT, because you're concatenating it to a string; you need to use bind variables, per the documentation:
open c_emp for
'select ID, NAME
from ' || DBMS_ASSERT.schema_name(p_schema) || '.EMP
where DATE_MAJ >= :1' using p_date;
This also gives you a lot more protection from SQL injection.

PL/SQL Dynamic Loop Value

My goal is to keep a table which contains bind values and arguments, which will later be used by dbms_sql. The below pl/sql example is basic, it's purpose is to illustrate the issue I am having with recalling values from prior loop objects.
The table account_table holds acccount information
CREATE TABLE account_table (account number, name varchar2(100)));
INSERT INTO mytest
(account, name)
VALUES
(1 ,'Test');
COMMIT;
The table MYTEST holds bind information
CREATE TABLE mytest (bind_value varchar2(100));
INSERT INTO mytest (bind_value) VALUES ('i.account');
COMMIT;
DECLARE
v_sql VARCHAR2(4000) := NULL;
v_ret VARCHAR2(4000) := NULL;
BEGIN
FOR I IN (
SELECT account
FROM account_table
WHERE ROWNUM = 1
) LOOP
FOR REC IN (
SELECT *
FROM mytest
) LOOP
v_sql := 'SELECT ' || rec.bind_value || ' FROM dual';
EXECUTE IMMEDIATE v_sql INTO v_ret;
dbms_output.put_line ('Account: ' || v_ret);
END LOOP;
END LOOP;
END;
/
I cannot store the name i.account and later use the value that object. My idea was to use NDS; but, while the value of v_sql looks ok (it will read "Select i.account from dual"), an exception of invalid identifier would be raised on i.account. Is there a way to get the value of the object? We are using Oracle 11g2.
Thanks!
Dynamic SQL will not have the context of your PL/SQL block. You cannot use identifiers that are defined in the PL/SQL block in your dynamic SQL statement. So you cannot reference i.account and reference the loop that you have defined outside of the dynamic SQL statement. You could, of course, dynamically generate the entire PL/SQL block but dynamic PL/SQL is generally a pretty terrible approach-- it is very hard to get that sort of thing right.
If you are trying to use the value of i.account, however, you can do something like
v_sql := 'SELECT :1 FROM dual';
EXECUTE IMMEDIATE v_sql
INTO v_ret
USING i.account;
That doesn't appear to help you, though, if you want to get the string i.account from a table.
Are you always trying to access the same table with the same where clause, but just looking for a different column each time? If so, you could do this:
p_what_I_want := 'ACCOUNT';
--
SELECT decode(p_what_I_want
,'ACCOUNT', i.account
, 'OTHER_THING_1', i.other_thing_1
, 'OTHER_THING_2', i.other_thing_2
, 'OTHER_THING_1', i.default_thing) out_thing
INTO l_thing
FROM table;
The above statement is not dynamic but returns a different column on demand...