How to handle array list in a dynamic sql query - sql

I want to create a dynamic query to handle array list.
create or replace TYPE p_type IS table of varchar2(4000) ;
CREATE OR REPLACE PROCEDURE test_proc_sk(
p_class_array IN p_type,
p_emp_record OUT SYS_REFCURSOR)
IS
lv_stmt VARCHAR2(100);
BEGIN
lv_stmt := 'Select * from dept where deptno = 10 ';
IF(p_class_array IS NOT NULL) THEN
lv_stmt := lv_stmt || 'AND dname IN (select column_value from table(' || p_class_array ||'))';
END IF;
dbms_output.put_line(lv_stmt);
OPEN p_emp_record FOR lv_stmt;
END;
It gives a compilation error
Error(9,5): PL/SQL: Statement ignored Error(9,23): PLS-00306: wrong
number or types of arguments in call to '||'
Please help

Use the MEMBER OF operator:
CREATE OR REPLACE PROCEDURE test_proc_sk(
p_class_array IN p_type,
p_emp_record OUT SYS_REFCURSOR
)
IS
BEGIN
OPEN p_emp_record FOR
SELECT *
FROM dept
WHERE deptno = 10
AND ( p_class_array IS EMPTY OR dname MEMBER OF p_class_array );
END;

You will have to bind the parameter p_class_array .In dynamic SQL you would prefix them with a colon (:). If you use EXECUTE IMMEDIATE or OPEN ... FOR, you will bind your parameters via position to avoid sql injection.
Also note that in your Select statement you are doing *, so while execution you must declare individual variable to hold the outcome. To aviod mistake you must declare the column name in the select statement as shown below:
CREATE OR REPLACE PROCEDURE test_proc_sk(
p_class_array IN p_type,
p_emp_record OUT SYS_REFCURSOR)
IS
lv_stmt VARCHAR2(200);
BEGIN
lv_stmt := 'Select deptno,dname from dept where deptno = 10 ';
IF(p_class_array.count > 0) THEN
lv_stmt := lv_stmt || ' AND dname IN (select column_value from table(:p_class_array))';
END IF;
dbms_output.put_line(lv_stmt);
OPEN p_emp_record FOR lv_stmt using p_class_array;
END;
/
Execution:
SQL> Select * from dept ;
SQL> /
DEPTNO DNAME
---------- ----------------------------------------------------------------------------------------------------
10 CTS
20 WIPRO
30 TCS
SQL> DECLARE
vr p_type:= p_type();
x SYS_REFCURSOR;
z VARCHAR2(10);
z1 number;
BEGIN
vr.extend(2);
vr (1) := 'CTS';
vr (2) := 'TCS';
test_proc_sk (vr, x);
LOOP
FETCH x INTO z1,z;
EXIT WHEN x%NOTFOUND;
DBMS_OUTPUT.PUT_LINE (z ||' ' ||z1);
END LOOP;
END;
/
SQL> /
Select deptno,dname from dept where deptno = 10 AND dname IN (select column_value from table(:p_class_array))
CTS 10
PL/SQL procedure successfully completed.

Related

pass correct format of date value returned from another function

I'm using V_SQL as parameter which stores SQL query as string and takes DATE input which is returned from another function.
Function which is returning date value:
----------------------------------------
FUNCTION RETURN_DATE(V_D DATE) RETURN DATE IS
IS_BUS CHAR(1);
V_CNT NUMBER(5);
V_DT DATE;
BEGIN
V_DT :=V_D;
WHILE IS_BUSINESS_DAY(V_DT) = 'N'
LOOP
V_DT := V_DT - 1;
END LOOP;
IF IS_BUSINESS_DAY(V_DT) = 'Y' THEN
V_DT := V_DT - 1;
END IF;
RETURN V_DT;
END RETURN_DATE;
V_SQL := 'SELECT A.ACCOUNT_TYPE, B.FIN_ELEM, A.ORG_UNIT_ID, A.GL_ACCOUNT_ID, B.CMN_COA_ID, B.PROD1, B.PROD2, B.PROD3, ' ||
'SUM(CURRENT_BAL) AS CB_SUM, SUM(AVG_BAL) AS AB_SUM, B.FLAG1 FROM DAILYGL_TEST A, AL_LOOKUP B '||
'WHERE A.GL_ACCOUNT_ID = B.GL_ACCT AND A.AS_OF_DATE = '||
**RETURN_DATE(V_RUN_DATE)** ||
' AND ROWNUM <=15 GROUP BY A.ACCOUNT_TYPE, B.FIN_ELEM, A.ORG_UNIT_ID, A.GL_ACCOUNT_ID,B.CMN_COA_ID, B.PROD1, B.PROD2, B.PROD3, A.AS_OF_DATE, B.FLAG1';
I am getting date returned as '29-AUG-2019' and when it is being supplied to this V_SQL query, it is throwing 'AUG not valid identifier issue'. Also, Date in AS_OF_DATE column is in MM/DD/YYYY format such 09/02/2019 for 02-SEP-2019.
Could you please help me in editing, formatting this code properly so this error can be removed. Let me know what I should change in RETURN_DATE(V_RUN_DATE) while supplying it to V_SQL.
Thanks in advance!
The way you are calling the function is wrong; it shouldn't be concatenated to the main query, but embedded into it. Have a look at this simplified example (line #5 is what you should pay attention to).
SQL> create or replace function return_date (v_d in varchar2)
2 return date
3 is
4 begin
5 return to_date(v_d, 'dd.mm.yyyy');
6 end;
7 /
Function created.
SQL> select ename, hiredate from emp where rownum = 1;
ENAME HIREDATE
---------- -------------------
SMITH 17.12.1980 00:00:00
SQL> declare
2 v_sql varchar2(500);
3 l_row emp%rowtype;
4 begin
5 v_sql := q'[select * from emp where hiredate = return_date('&v_run_date') ]' ||
6 ' and rownum = 1';
7 dbms_output.put_Line(v_sql);
8 execute immediate v_sql into l_row;
9 dbms_output.put_line(l_row.ename);
10 end;
11 /
Enter value for v_run_date: 17.12.1980
select * from emp where hiredate = return_date('17.12.1980') and rownum = 1
SMITH
PL/SQL procedure successfully completed.
SQL>

How to use dynamic table of every month in the insert statement while one static value we are passing in one column

In one Stored proc, I used insert into statement i declare one variable as v_tbl_name which is dynamic. I am going to use this dynamic table in insert into block while we are passing one static value in one column but getting error. Here is the sample code-
declare
v_tbl_name varchar2(5) := NULL;
v_gen_tbl constant varchar2(50) := 'arch_tbl_mon'; --table name
v_sql varchar2(10000);
begin
v_tbl_name := v_gen_tbl || '_' || TO_CHAR(SYSDATE, 'MON');
v_sql := 'insert into tbl --- another table going to insert
(select ''abc'' as col1, ........
from '||v_tbl_name||');'
execute immediate v_sql;
commit;
end;
Here 'abc' static value as column. After executing the above code, It is generating an err like Encountered the symbol "Execute".
Do it this way:
declare
v_tbl_name varchar2(5) := NULL;
v_gen_tbl constant varchar2(50) := 'arch_tbl_mon'; --table name
v_sql varchar2(1000);
begin
v_tbl_name := v_gen_tbl || '_' || TO_CHAR(SYSDATE, 'MON');
v_sql:= 'insert into tbl --- another table going to insert
select 'abc' as col1, ........
from '||v_tbl_name;
execute immediate v_sql;
commit;
end;
Demo:
declare
v_tbl_name varchar2(5) := NULL;
v_gen_tbl constant varchar2(50) := 'arch_tbl_mon'; --table
v_sql varchar2(1000);
begin
v_tbl_name := 'DEPT';
v_sql :='insert into tbl select * from '||v_tbl_name;
execute immediate v_sql;
end;
Exec:
SQL> select * from tbl;
DEPTNO DNAME LOC
---------- -------------------- --------------------
1 XXX YYY
Edit:
Here 'abc' static value as column so that's why not able to execute
the stored proc. It is generating an err like Encountered the symbol '
abc '.
See below demo:
Table tbl is being populated with static value abc from select statement:
SQL> select * from tbl;
DEPTNO DNAME LOC B
---------- -------------------- -------------------- ---
After Execution:
declare
v_tbl_name varchar2(5) := NULL;
v_gen_tbl constant varchar2(50) := 'arch_tbl_mon'; --table
v_sql varchar2(1000);
begin
v_tbl_name := 'DEPT';
v_sql :='insert into tbl (deptno,dname,loc,b) select a.*, ''abc'' from '||v_tbl_name||' a';
execute immediate v_sql;
Commit;
end;
OUTPUT
SQL> SELECT * FROM TBL;
DEPTNO DNAME LOC B
---------- -------------------- -------------------- ---
100 Executive USA abc
Final Edit:
You didnot follow what I showed to you hence you landed into the problem. Follow my inline comments. Please replace the column name where mentioned and it would be resolved.
declare
v_tbl_name varchar2(5) := NULL;
v_gen_tbl constant varchar2(50) := 'arch_tbl_mon'; --table name
v_sql varchar2(10000);
begin
v_tbl_name := v_gen_tbl || '_' || TO_CHAR(SYSDATE, 'MON');
v_sql := 'insert into tbl(col1,.....<all columns>) --- another table going to insert
select ''abc'' as col1, a.col2........<other columns a.<columns>
from '||v_tbl_name||' a';
execute immediate v_sql;
commit;
end;

Display result from loop tables (oracle, pl/sql)

I'm try to loop some tables and run select as below:
set serveroutput on
declare
type tables_names is table of varchar2(30);
type selectTable is table of varchar2(30);
tName tables_names;
sTableName selectTable;
begin;
tName := tables_names('PERIOD','SETTING','RAP','LOG');
sTableName := selectTable('m_table1','m_table2','m_table3','m_table4','m_table5');
for i in 1..tName.count loop
for j in 1..sTableName.count loop
select col10, count(*) from user.sTableName(j)
where table_name = tName(i) group by col10;
end loop;
end loop;
end;
I got error:PL/SQL: ORA-00933.
Can you please tell me how can I correctly run PL/SQL procedure to have displayed result from my select?
UPDATE: looking result
Normally, to get this I need to run below select's:
select column_name,
count(*) as countColumn
from user.m_table1 where table_name = 'PERIOD' group by column_name;
select column_name,
count(*) as countColumn
from user.m_table2 where table_name = 'PERIOD' group by column_name;
Oracle complains (ORA-00933) that command isn't properly ended. That's probably because of a semi-colon behind the BEGIN; also, you lack the INTO clause.
I'm not sure what PERIOD, SETTING, ... are opposed to m_table1, m_table2, ... Which ones of those are table names? What are those other values, then?
Anyway: here's an example which shows how to do something like that - counting rows from tables. Try to adjust it to your situation, or - possibly - add some more info so that we'd know what you are doing.
SQL> set serveroutput on
SQL> declare
2 tname sys.odcivarchar2list := sys.odcivarchar2list();
3 l_cnt number;
4 l_str varchar2(200);
5 begin
6 tname := sys.odcivarchar2list('EMP', 'DEPT');
7
8 for i in 1 .. tname.count loop
9 l_str := 'select count(*) from ' || tname(i);
10 execute immediate l_str into l_cnt;
11 dbms_output.put_line(tname(i) ||': '|| l_cnt);
12 end loop;
13 end;
14 /
EMP: 14
DEPT: 4
PL/SQL procedure successfully completed.
SQL>
[EDIT: added GROUP BY option]
Here you go; as EMP and DEPT share the DEPTNO column, I chose it for a GROUP BY column.
SQL> declare
2 tname sys.odcivarchar2list := sys.odcivarchar2list();
3 type t_job is record (deptno varchar2(20), cnt number);
4 type t_tjob is table of t_job;
5 l_tjob t_tjob := t_tjob();
6 l_str varchar2(200);
7 begin
8 tname := sys.odcivarchar2list('EMP', 'DEPT');
9
10 for i in 1 .. tname.count loop
11 l_str := 'select deptno, count(*) from ' || tname(i) ||' group by deptno';
12 execute immediate l_str bulk collect into l_tjob;
13
14 for j in l_tjob.first .. l_tjob.last loop
15 dbms_output.put_Line('Table ' || tname(i) || ': Deptno ' || l_tjob(j).deptno||
16 ': number of rows = '|| l_tjob(j).cnt);
17 end loop;
18
19 end loop;
20 end;
21 /
Table EMP: Deptno 30: number of rows = 6
Table EMP: Deptno 20: number of rows = 5
Table EMP: Deptno 10: number of rows = 3
Table DEPT: Deptno 10: number of rows = 1
Table DEPT: Deptno 20: number of rows = 1
Table DEPT: Deptno 30: number of rows = 1
Table DEPT: Deptno 40: number of rows = 1
PL/SQL procedure successfully completed.
SQL>
You are probably looking for something like this. Note that you can't run a simple select statement inside a PL/SQL without INTO clause. use a refcursor and DBMS_SQL.RETURN_RESULT
DECLARE
TYPE tables_names IS TABLE OF VARCHAR2 (30);
TYPE selectTable IS TABLE OF VARCHAR2 (30);
tName tables_names;
sTableName selectTable;
rc SYS_REFCURSOR;
BEGIN
tName :=
tables_names ('PERIOD',
'SETTING',
'RAP',
'LOG');
sTableName :=
selectTable ('m_table1',
'm_table2',
'm_table3',
'm_table4',
'm_table5');
FOR i IN 1 .. tName.COUNT
LOOP
FOR j IN 1 .. sTableName.COUNT
LOOP
OPEN rc FOR
'select col10, count(*) from '||USER||'.'
|| sTableName (j)
|| ' where table_name = '''
|| tName (i)
|| ''' group by col10';
DBMS_SQL.RETURN_RESULT (rc);
END LOOP;
END LOOP;
END;
/

How to set an object as an out parameter?

I have written a procedure in PL/SQL and want to return a EMP type object. Is it possible to do that? If yes, how can I do it?
Here is the code:
CREATE OR REPLACE
PROCEDURE get_emp_rs (p_deptno IN emp.deptno%TYPE,
p_recordset OUT emp_det) AS
emp_details emp_det;
BEGIN
OPEN p_recordset FOR
SELECT ename,
empno
FROM emp
WHERE deptno = p_deptno
ORDER BY ename;
fetch p_recordset into emp_details;
--exit when p_recordset%notfound;
--end loop;
--for indx in p_recordset
--loop
emp_details.ename:= 'test';
--end loop;
END get_emp_rs;
/
SET SERVEROUTPUT ON SIZE 1000000
DECLARE
l_cursor emp_det;
--l_cur emp_det;
--l_ename emp.ename%TYPE;
--l_empno emp.empno%TYPE;
l_deptno emp.deptno%TYPE;
BEGIN
l_cur:=get_emp_rs ('30',
l_cursor);
dbms_output.put_line('low');
/*LOOP
FETCH l_cursor
INTO l_ename, l_empno, l_deptno;
EXIT WHEN l_cursor%NOTFOUND;*/
DBMS_OUTPUT.PUT_LINE(l_cursor.ename || ' | ' || l_cursor.empno);
end;
/
I want to get the ename and empno after finally update in the procedure.
How can I do it? If there is any better way of doing this please suggest me.
And also please suggest me how I can do it in this way. I cannot use any functions here that's the only obligation. Also please let me know if there is a way of doing this using.
In your example (Table EMP) a single EMP Record shouldn't be selected by the department number. Use the EMPNO instead.
The following is a quick solution and hopefully self-explainable:
SET SERVEROUTPUT ON;
SET FEEDBACK OFF;
CLEAR;
--Define the dataset object
CREATE TYPE EMP_DET AS OBJECT (
EMPNO NUMBER(4),
ENAME VARCHAR2(10)
);
/
--Define a collection of dataset objects
CREATE TYPE EMP_DET_LIST IS TABLE OF EMP_DET;
/
--Get a SINGLE record into the OUT-Variable identified by EMP PK EMPNO
CREATE OR REPLACE PROCEDURE GET_EMP_RS(P_EMPNO IN EMP.EMPNO%TYPE,
P_RECORDSET IN OUT EMP_DET) AS
BEGIN
--Create the return object inside SQL
SELECT EMP_DET(EMPNO, ENAME)
INTO P_RECORDSET
FROM EMP
WHERE EMPNO = P_EMPNO;
P_RECORDSET.ENAME := 'test';
EXCEPTION
WHEN NO_DATA_FOUND THEN
--Return NULL if employee not found
P_RECORDSET := NULL;
END GET_EMP_RS;
/
--Get a LIST OF employees by department
CREATE OR REPLACE PROCEDURE GET_EMP_LIST(P_DEPTNO IN EMP.DEPTNO%TYPE,
P_RECORDLIST OUT EMP_DET_LIST) AS
TYPE C_CURSOR IS REF CURSOR; -- <-- For the explicit cursor solution only
C_EMP_RS C_CURSOR; -- <-- For the explicit cursor solution only
V_RS EMP_DET; -- <-- For the explicit cursor solution only
BEGIN
--Initialize out object
P_RECORDLIST := EMP_DET_LIST();
--Create the return object inside SQL
--via bulk collect
/*
SELECT EMP_DET(EMPNO,ENAME)
BULK COLLECT INTO INTO P_RECORDLIST
FROM EMP
WHERE DEPTNO = P_DEPTNO;
*/
--with manipulation of records
--use a FOR-LOOP with implizit cursor
/*
FOR L_RS IN (SELECT EMP_DET(EMPNO, ENAME) EMP_RS
FROM EMP
WHERE DEPTNO = P_DEPTNO) LOOP
L_RS.EMP_RS.ENAME := 'TEST';
P_RECORDLIST.EXTEND;
P_RECORDLIST(P_RECORDLIST.LAST) := L_RS.EMP_RS;
NULL;
END LOOP;
*/
--or define an explicit cursor and LOOP-FETCH
OPEN C_EMP_RS FOR
SELECT EMP_DET(EMPNO, ENAME) EMP_RS
FROM EMP
WHERE DEPTNO = P_DEPTNO;
LOOP
FETCH C_EMP_RS
INTO V_RS;
EXIT WHEN C_EMP_RS%NOTFOUND;
V_RS.ENAME := 'TEST';
P_RECORDLIST.EXTEND;
P_RECORDLIST(P_RECORDLIST.LAST) := V_RS;
END LOOP;
CLOSE C_EMP_RS;
END GET_EMP_LIST;
/
--**************************
-- Test
--**************************
DECLARE
L_CURSOR EMP_DET;
L_LIST EMP_DET_LIST;
BEGIN
DBMS_OUTPUT.PUT_LINE('---- Single EMP ----');
GET_EMP_RS(7369, L_CURSOR);
IF (L_CURSOR IS NOT NULL) THEN
DBMS_OUTPUT.PUT_LINE(L_CURSOR.ENAME || ' | ' || L_CURSOR.EMPNO);
END IF;
DBMS_OUTPUT.PUT_LINE('---- EMP List ----');
GET_EMP_LIST(30, L_LIST);
IF (L_LIST.count > 0 ) THEN
FOR L_I IN L_LIST.FIRST .. L_LIST.LAST LOOP
DBMS_OUTPUT.PUT_LINE(L_LIST(L_I).ENAME || ' | ' || L_LIST(L_I).EMPNO);
END LOOP;
END IF;
END;
/
DROP PROCEDURE GET_EMP_RS;
DROP PROCEDURE GET_EMP_LIST;
DROP TYPE EMP_DET_LIST;
DROP TYPE EMP_DET;
Output:
---- Single EMP ----
test | 7369
---- EMP List ----
TEST | 7499
TEST | 7521
TEST | 7654
TEST | 7698
TEST | 7844
TEST | 7900
SQL>

procedure to delete duplicate records using dynamic sql

i have successfully implemented a proc which can delete delete duplicate records from a table .
Carrying on i am further trying to generalize this procedure using Dynamic sql But i get the following error when compiling itself .
ERROR line 13, col 7, ending_line 14, ending_col 14, Found ''SELECT ROWID, ERR_MOD
FROM '', Expecting: ( SELECT -or- WITH
Here is my code :-
CREATE OR REPLACE PROCEDURE BIOCON.proc_dedup (
p_tblname IN VARCHAR2,
p_cname IN varchar2,
p_cvalue IN varchar2,
p_out OUT VARCHAR2:=0
)
IS
v_cnt NUMBER;
CURSOR TMP
IS
'SELECT ROWID, ERR_MOD
FROM ' || p_tblname||
'WHERE '|| p_cname '='||''p_cvalue '';
BEGIN
BEGIN
'SELECT COUNT ( * )
INTO v_cnt
FROM ' || p_tblname||
'WHERE '|| p_cname' = '||''p_cvalue'';
EXCEPTION
WHEN OTHERS
THEN
p_out:=1;
end;
FOR REC IN tmp
LOOP
IF v_cnt = 1
THEN
EXIT;
ELSE
'DELETE FROM '|| p_tblname||
'WHERE ROWID = REC.ROWID';
END IF;
v_cnt := v_cnt - 1;
END LOOP;
EXCEPTION
WHEN OTHERS
THEN
p_out := 2;
END;
/
Hi you cannot create a dynamic query for normal cursors in such cases you should use ref cursors and follow the the steps below
--create a query string
--open the ref cursor
and please use execute immediate for execution of delete statements...
CREATE OR REPLACE PROCEDURE BIOCON.proc_dedup (
p_tblname IN VARCHAR2,
p_cname IN varchar2,
p_cvalue IN varchar2,
p_out OUT VARCHAR2:=0
IS
v_cnt NUMBER;
v_string varchar2(200);
TYPE ref_cur IS REF CURSOR
ref_cursor_name ref_cur;
BEGIN
v_string:='SELECT ROWID, ERR_MOD FROM ' || p_tblname|| 'WHERE '|| p_cname
'='||''p_cvalue '';
OPEN cursor_variable_name FOR v_string;
.....
.....
EXECUTE IMMEDIATE 'DELETE FROM '|| p_tblname|| 'WHERE ROWID = REC.ROWID';--you need execute immediate for this
.....
.....