PL/SQL duplicate - sql

I'm executing the following PL/SQL script on sqlplus :
declare
cursor c is select sal, empno, ename from emp where ((comm is null and sal>2000) or (comm is not null and (sal+comm)>2000));
v_sal emp.sal%type;
v_empno emp.sal%type;
v_ename emp.ename%type;
begin
open c;
loop
fetch c into v_sal,v_empno,v_ename;
insert into temp values(v_sal,v_empno,v_ename);
exit when(c%notfound);
end loop;
close c;
end;
/
I obtain all the n-uplets I want but the last one is duplicated.

Put the exit statement before insert.

Related

PL/SQL cursor creation fails

Cursor creation fails in SQL Developer with PL/SQL
CREATE OR REPLACE PROCEDURE print_jobs(d_name varchar2) IS
DECLARE
CURSOR curs1
IS
SELECT e.job, d.dname
FROM nikovits.emp e
JOIN nikovits.dept d ON e.deptno = d.deptno where d.dname = d_name;
rec curs%ROWTYPE;
BEGIN
OPEN curs1;
LOOP
fetch curs1 into curs;
exit when curs1%NOTFOUND;
dbms_output.put_line(to_char(rec.deptno));
END LOOP;
CLOSE curs1;
end;
Error : Encountered the symbol "DECLARE" when expecting one of the following: begin function pragma procedure subtype type
#Ferid Qenberli - When you create a procedure , you need to omit DECLARE and use AS. Try this:
CREATE OR REPLACE PROCEDURE print_jobs(d_name varchar2)
as
CURSOR curs1
IS
SELECT e.job, d.dname
FROM nikovits.emp e
JOIN nikovits.dept d ON e.deptno = d.deptno where d.dname = d_name;
rec curs1%ROWTYPE;
BEGIN
OPEN curs1;
LOOP
fetch curs1 into curs;
exit when curs1%NOTFOUND;
dbms_output.put_line(to_char(rec.deptno));
END LOOP;
CLOSE curs1;
end;
/
Please use only AS or IS and remove DECLARE keyword.
CREATE OR REPLACE PROCEDURE print_jobs(d_name varchar2) IS
CURSOR curs1 IS
SELECT e.job, d.dname
FROM nikovits.emp e
JOIN nikovits.dept d ON e.deptno = d.deptno where d.dname = d_name;
rec curs%ROWTYPE;
BEGIN
OPEN curs1;
LOOP
fetch curs1 into curs;
exit when curs1%NOTFOUND;
dbms_output.put_line(to_char(rec.deptno));
END LOOP;
CLOSE curs1;
END;

How to handle array list in a dynamic sql query

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.

sql select statement as bind variable for dynamic plsql block

I am trying to run plsql anonymous block using execute immediate and the plsql block contains a bind variable for which the value is a sql select statement. But it seems this does not work. Is there any solution for to solve this.
E.g.
BEGIN
V_SQL:='SELECT emp_id FROM emp WHERE dept_id=10;
PLSQL_BLOCK:='DECLARE
type emp_type
IS
TABLE OF NUMBER;
emp_id emp_type;
BEGIN
EXECUTE IMMEDIATE :1 BULK COLLECT INTO emp_id;
END';
EXECUTE IMMEDIATE PLSQL_BLOCK USING V_SQL;
If I understand well, you need to run an entire dynamic PLSQL block by using as SQL query as a bind variable; if so, you can try this way:
SQL> declare
2 vPlSqlBlock varchar2(10000);
3 vSQL varchar2(1000);
4 BEGIN
5 vSQL:='SELECT 1 from dual';
6 --
7 vPlSqlBlock:='DECLARE
8 type emp_type IS TABLE OF NUMBER;
9 emp_id emp_type;
10 vSQLDyn varchar2(1000) := :1;
11 BEGIN
12 EXECUTE IMMEDIATE vSQLDyn BULK COLLECT INTO emp_id;
13 --
14 /* whatever you need to do in your block */
15 for i in emp_id.first .. emp_id.last loop
16 dbms_output.put_line(emp_id(i));
17 end loop;
18 END;';
19
20 EXECUTE IMMEDIATE vPlSqlBlock USING vSQL;
21 end;
22 /
1
PL/SQL procedure successfully completed.
SQL> declare
2 vPlSqlBlock varchar2(10000);
3 vSQL varchar2(1000);
4 BEGIN
5 vSQL:='SELECT 1 from dual';
6 --
7 vPlSqlBlock:='DECLARE
8 type emp_type IS TABLE OF NUMBER;
9 emp_id emp_type;
10 vSQLDyn varchar2(1000) := :1;
11 BEGIN
12 EXECUTE IMMEDIATE vSQLDyn BULK COLLECT INTO emp_id;
13 /* this does nothing */
14 END;';
15
16 EXECUTE IMMEDIATE vPlSqlBlock USING vSQL;
17 end;
18 /
PL/SQL procedure successfully completed.
SQL>
Plz try this.Hope this helps.
set serveroutput on;
set define on;
DECLARE
V_SQL varchar2(1000):='SELECT emp_id from emp where deptno = 10';
PLSQL_BLOCK varchar2(1000):='DECLARE
type emp_type
IS
TABLE OF NUMBER;
emp_id emp_type;
BEGIN
EXECUTE IMMEDIATE :1 BULK COLLECT INTO emp_id;
END;';
BEGIN
EXECUTE IMMEDIATE PLSQL_BLOCK USING V_SQL;
END;
You have to first declare the variables you want to use in your execution block.
Try this:
DECLARE
V_SQL VARCHAR2(1000) := 'SELECT emp_id FROM emp WHERE dept_id=10';
PLSQL_BLOCK varchar2(1000) :='DECLARE type emp_type IS TABLE OF integer; emp_id emp_type;'
|| ' BEGIN EXECUTE IMMEDIATE :1 BULK COLLECT INTO emp_id; END;';
BEGIN
EXECUTE IMMEDIATE PLSQL_BLOCK USING V_SQL;
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>

CURSOR and LOOP don't work correctly on Oracle database

I have a problem with my PL-SQL script on Oracle database. That's not work correctly. I've heard one statement is useless. I can't figure out which one.
DECLARE
CURSOR emp_cur IS
SELECT ename, deptno, empno
FROM emp
WHERE sal <
2500;
emp_rec emp_cur%ROWTYPE;
BEGIN
FOR emp_rec IN emp_cur
LOOP
give_raise (emp_rec.empno, 10000);
END LOOP;
END;
you could try this way. It is not necessary declare explicity the cursor if you only use it as an iterator in a for loop. I think the same as OracleUser2 about the explicit declaration of emp_rec.
DECLARE
BEGIN
FOR emp_rec IN (select ename, deptno, empno from emp where sal < 2500)
LOOP
give_raise (emp_rec.empno, 10000);
END LOOP;
END;
Thanks
Regards