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.
Related
I have to get the min and max dates for the data stored in all the database tables and display same along with the table names. I have written below function to do same
CREATE OR REPLACE FUNCTION data_bound(tbl_name IN VARCHAR2) RETURN VARCHAR2
AS
reqdates VARCHAR2;
BEGIN
EXECUTE IMMEDIATE 'SELECT concat(min(rec_date),max(rec_date)) from ' || tbl_name INTO reqdates;
RETURN reqdates;
END;
And I am trying to fetch data using below.
SELECT table_name, data_bound(table_name) FROM user_tables;
But my function does seems to be working, getting multiple errors. Please can you advise what's wrong here and if there's another better approach.
You can use:
CREATE FUNCTION data_bound(
tbl_name IN VARCHAR2
) RETURN VARCHAR2
AS
reqdates VARCHAR2(39);
BEGIN
EXECUTE IMMEDIATE 'SELECT TO_CHAR(min(rec_date), ''YYYY-MM-DD HH24:MI:SS'')
|| ''-'' || TO_CHAR(max(rec_date), ''YYYY-MM-DD HH24:MI:SS'')
FROM ' || DBMS_ASSERT.SIMPLE_SQL_NAME(tbl_name)
INTO reqdates;
RETURN reqdates;
END;
/
Which, if you have the table:
CREATE TABLE table_name (rec_date) AS
SELECT TRUNC(SYSDATE, 'YYYY') FROM DUAL UNION ALL
SELECT SYSDATE FROM DUAL;
Then:
SELECT data_bound('TABLE_NAME') FROM DUAL;
Outputs:
DATA_BOUND('TABLE_NAME')
2022-01-01 00:00:00-2022-04-19 18:57:25
db<>fiddle here
Error 1: data_bound() function is defined. data_retention() function is called
Error 2: tbl_name is taken as input, but not used (t_name is used once)
Error 3: 'from' keyword is missing in the execute immedeate statement
Error 4: 'into' keyword should be placed before 'from tableName'
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.
I have below procedure for which i am passing db link as a parameter and create dynamic sql statement. While executing the procedure i am getting error as ORA-00904: "DB_CONNECTION_NAME": invalid identifier. I have declared the variable but still i am getting this error.
CREATE OR REPLACE PROCEDURE "EXT_SDR_RECEIVED"(in_db_link IN VARCHAR2)
AS
last_sm_id NUMBER := 0;
last_capt_date DATE;
l_sql VARCHAR2(5000);
db_connection_name VARCHAR2(100);
BEGIN
SELECT db_link INTO db_connection_name
FROM rator_monitoring_configuration.db_connection
WHERE db_link = in_db_link;
--DELETE DATA FROM TEMP_SDR_RECEIVED
DELETE FROM temp_sdr_received WHERE create_date < SYSDATE - 7;
-- first retrieve the last id (of the newest record) which has been imported at last extraction
SELECT last_task_id INTO last_sm_id
FROM capturing WHERE db_table = 'TEMP_SDR_RECEIVED';
SELECT capturing_date INTO last_capt_date
FROM capturing WHERE db_table = 'TEMP_SDR_RECEIVED';
dbms_output.PUT_LINE('DB' || db_connection_name);
-- retrieve all new records from remote SDR_O2 table and insert it into TEMP_SDR_RECEIVED where ID is greater than LAST_SM_ID
l_sql := 'INSERT INTO TEMP_SDR_RECEIVED(ID,RATING_CODE,A_NUMBER,CREATE_DATE,VOUCHER_ATTEMPT_ID,RATOR_BRAND_ID,BRAND_ID,STATUS_DESCRIPTION,ACCOUNT_PAYMENT_ID,SUBSCRIPTION_ID,DB_LINK)
SELECT SD.ID,SD.RATING_CODE,SD.A_NUMBER,to_date(substr(SD.ID, 1, 8), ''YYYYMMDD''),VA.ID,VA.BRAND_ID,BR.BRAND_ID,VA.STATUS_DESCRIPTION,VA.ACCOUNT_PAYMENT_ID,VA.SUBSCRIPTION_ID,DB_CONNECTION_NAME
FROM SDR_O2#' || db_connection_name || ' SD
JOIN VOUCHER_ATTEMPT#' || db_connection_name || ' VA
ON SD.ID = VA.SDR_ID,
RATOR_MONITORING_CONFIGURATION.BRAND BR
WHERE VA.BRAND_ID IS NOT NULL
AND BR.RATOR_BRAND_ID = VA.BRAND_ID
AND SD.RATING_CODE=''VOUCHER''
AND VA.STATUS_DESCRIPTION = ''USSD voucher''
AND SD.ID > LAST_SM_ID';
EXECUTE IMMEDIATE l_sql;
END ext_sdr_received;
You are referencing DB_CONNECTION_NAME in the select part of your dynamic query (look after VA.SUBSCRIPTION_ID). Do any of your 3 tables have that column? I suspect not.
I suspect that you wanted to select the value in the DB_CONNECTION_NAME variable instead. To do that, change that last part of the SELECT in your dynamic query like this:
'...,VA.SUBSCRIPTION_ID, ''' || DB_CONNECTION_NAME || '''
...
You may also want to look into how execute immediate supports parameter binding, so that, where possible, you can avoid having to write this ugly string concatenation code.
Also, I notice you are mixing join notations. That's asking for trouble. Stick to ANSI JOIN syntax.
I am trying to display the duplicate records using dynamic sql(execute immediate). I am getting 'An identifier with more than 30 characters was specified' error. What am I doing wrong with the dynamic sql?
CREATE OR REPLACE PROCEDURE FIND_DUP(P_TABLE IN VARCHAR2, P_COLUMN IN VARCHAR2)
AS
stmt_txt varchar2(4000);
BEGIN
stmt_txt:= 'select'
||p_column
|| 'from'
||p_table
|| 'group by'
||p_column
||'having count(*)>1';
execute immediate stmt_txt;
end;
/
EXECUTE FIND_DUP('EMPLOYEES','FIRST_NAME');
You're missing some spaces in your query.
stmt_txt:= 'select '
||p_column
|| ' from '
||p_table
|| ' group by '
||p_column
||' having count(*)>1';
Without the spaces your query would end up as selectFIRST_NAMEfromEMPLOYEESgroup byFIRST_NAMEhaving count(*)>1, which to Oracle looks like an identifier with more than 30 characters.
Hi the first thing whih should be kept in mind while using dynamic sql is ALWAYS print the sql generated as it will give you the idea what query exactly you are running. This code might help you to solve your query as you have done almost everything right only you were missing with some spaces.
CREATE OR REPLACE
PROCEDURE FIND_DUP(
P_TABLE IN VARCHAR2,
P_COLUMN IN VARCHAR2)
AS
stmt_txt VARCHAR2(4000);
BEGIN
stmt_txt:= 'select' ||' ' ||p_column ||' ' ||'from' ||' ' ||p_table;
EXECUTE immediate stmt_txt;
DBMS_OUTPUT.PUT_LINE(STMT_TXT);
END;
For the output
BEGIN
FIND_DUP('DUAL','SYSTIMESTAMP');
END;
---------------------------------------------------------------------------
OUTPUT
select SYSTIMESTAMP from DUAL
Statement processed.
0.01 seconds
---------------------------------------------------------------------------
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