Please, help me to convert this loop statement:
BEGIN
FOR employee_rec in (SELECT * FROM BONUS)
LOOP
IF employee_rec.BONVALUE > 500 THEN
UPDATE BONUS
SET BONUS.TAX = BONUS.BONVALUE * 0.12 WHERE employee_rec.BONVALUE = BONUS.BONVALUE;
ELSIF
-- ...
END IF;
END LOOP;
END;
into statement where CURSOR being used.
What you have is a loop based on an implicit cursor definition. It sounds like you want a loop based on an explicit cursor definition.
As mentioned in the comments. Just define your cursor explicitly in the declaration section.
DECLARE
CURSOR cur_bonus IS
SELECT *
FROM bonus;
BEGIN
FOR employee_rec IN cur_bonus
LOOP
IF employee_rec.BONVALUE > 500 THEN
UPDATE BONUS
SET BONUS.TAX = BONUS.BONVALUE * 0.12 WHERE employee_rec.BONVALUE = BONUS.BONVALUE;
ELSIF
-- ...
END IF;
END LOOP;
END;
Related
I am trying to use Dynamic query in my code below but getting error (00900. 00000 - "invalid SQL statement"). Kindly suggest where i am mistaking in the code.
create or replace PROCEDURE CreateInsertTmpTable
AS
crttmp VARCHAR2(200);
intrtmp VARCHAR2(200);
printTableValues VARCHAR2(1000);
BEGIN
crttmp := 'CREATE GLOBAL TEMPORARY TABLE my_temp_table ON COMMIT PRESERVE ROWS AS SELECT * FROM VWBLKDATA WHERE 1=0';
EXECUTE IMMEDIATE crttmp;
intrtmp := 'INSERT INTO my_temp_table SELECT * FROM VWBLKDATA';
EXECUTE IMMEDIATE intrtmp;
printTableValues := ' for data in(SELECT * from my_temp_table)
loop
dbms_output.put_line(data.ID);
end loop';
EXECUTE IMMEDIATE printTableValues;
COMMIT;
END CreateInsertTmpTable;
I think you're overdoing the EXECUTE IMMEDIATE; you can run INSERT statements and PL/SQL without them like:
begin
for i in 1..10 loop
insert into test (some_column) values (to_char(i));
end loop;
end;
But anyways, it looks like you're last EXECUTE IMMEDIATE is trying to execute a partial PL/SQL anonymous block; it's missing a "begin" and "end;"
I would suggest just executing the for loop like so:
for data in (SELECT * from my_temp_table)
loop
dbms_output.put_line(data.ID);
end loop;
or else you'll need to add a begin/end around it in the text (and the "end loop" needs a trailing ";"):
printTableValues := 'begin
for data in (SELECT * from my_temp_table)
loop
dbms_output.put_line(data.ID);
end loop;
end;';
I am trying to run a query based on the day
For example
if it is the first of the month then select * from thistable
if it is the 2nd of the month then select * from thattable
Etc
What would be the best way to do this?
Apologize in advance if this is vague, new to PL/SQL. I know how to do it in tsql.
I've tried below
DECLARE
DATENUM INT := 1;
begin
if DATENUM = 1
then
select * from thistable;
else
select '* from thattable;
end if;
end;
DECLARE
v NUMBER;
BEGIN
v:=To_number(To_char(SYSDATE,'dd')) ;
IF v=1 THEN
--statement_1-----
ELSIF v=2 THEN
NULL;
--statement_2-----
END IF;
END;
Note : In PL/SQL INTO clause should be there in select.
How to read array of n elements dynamically and display the elements in plsql.
Below is my code (i am new to plsql programming)
set serveroutput on
set verify on
declare
type myarray is table of number index by binary_integer;
x myarray;
i pls_integer;
n number;
begin
-- populate array
dbms_output.put_line('Enter number of array elements');
n := &n;
dbms_output.put_line('Enter elements one by one');
for i in 1..n loop
dbms_output.get_lines(&&x(i),n);
end loop;
i :=0;
-- print array
loop
i := i + 1;
begin
dbms_output.put_line(x(i));
exception
when no_data_found then exit;
end;
end loop;
end;
/
quit;
I stumble into the same issue and wanted to try something.
You could do this with a brutal approach that consists in using you shell to call the client (i.e. sqlplus) any time you want to add a value to your array. I wrote a little package that stores the values of the array in a table. Then you can change the way you store the values, but principle would remain the same:
-- this package uses "execute immediate" statements for everything
-- because it assumes the temporary table T_MY_ARRAY can be non-existent.
create or replace package my_array
authid current_user
is
TYPE a_TBL_Number IS TABLE OF Number INDEX BY BINARY_INTEGER;
procedure init;
procedure add(v in number);
procedure display;
function to_var return a_TBL_Number;
end my_array;
/
create or replace package body my_array
is
procedure init is
-- create table if needed, then make sure its empty;
begin
begin
execute immediate 'create table T_MY_ARRAY(c1 number)';
exception when others then
-- dbms_output.put_line(sqlerrm);
-- we're never sure the temp. table already exists. So create it and catch if existing.
null;
end;
execute immediate 'truncate table T_MY_ARRAY';
end init;
procedure add(v in number) is
-- add new value
begin
execute immediate 'insert into T_MY_ARRAY (c1) values ('||v||')';
end add;
function to_var return a_TBL_Number is
-- hand out an array with the values
t_TBL_n a_TBL_Number;
begin
execute immediate 'select c1 from T_MY_ARRAY ' bulk collect into t_TBL_n;
return t_TBL_n;
end to_var;
procedure display is
t_TBL_n a_TBL_Number;
begin
t_TBL_n:=my_array.to_var();
for i in 1..t_TBL_n.count loop
dbms_output.put_line(t_TBL_n(i));
end loop;
end display;
end my_array;
/
Then here is how you could call this from a shell (here it is for an old ksh):
read SQL_CONNECT?"Enter your SQL connection string:"
read n?"Enter number of array elements:"
# first initialize the array:
sqlplus -s ${SQL_CONNECT} <<_EOF
set serveroutput off
set termout off
set feedback off
begin
my_array.init;
end;
/
_EOF
# then loop on the given number of elements
typeset -i10 i=0
while [[ ${i} -lt ${n} ]]
do
i=i+1
echo iter:${i}
read MY_VALUE?"Enter elements one by one:"
sqlplus -s ${SQL_CONNECT} <<_EOF
set serveroutput off
set termout off
set feedback off
begin
my_array.add(${MY_VALUE});
end;
/
commit;
_EOF
done
# at the end, use stored values to display result:
sqlplus -s ${SQL_CONNECT} <<_EOF
set serveroutput on
set feedback off
begin
dbms_output.put_line('---------------');
dbms_output.put_line('your input was:');
my_array.display;
end;
/
_EOF
I am trying to figure out how loop through a hierarchy, I don't know how to put in PLSQL. What I am trying to achieve: I want to know what department is 10 steps above me in a hierarchy:
Say I have a table with a department and a parent department. I want to perform this kind of operation:
select my_department from table_departments as v_department
FOR counter in 1...9
LOOP
v_department:=
(SELECT parent_department
FROM table_department_hierarchy
WHERE child_department=v_department)
END LOOP as top_department;
I can't figure out the correct syntax, is there a brave soul out there who can help me?
Your method with corrected PL/SQL syntax would be something like:
begin
select my_department into v_department from table_departments;
FOR counter in 1...9
LOOP
SELECT parent_department
INTO v_department
FROM table_department_hierarchy
WHERE child_department=v_department;
END LOOP:
END;
However you could perhaps get it all in one statement something like this:
SELECT parent_department
INTO v_department
FROM
( SELECT parent_department, level as lvl
FROM table_department_hierarchy
CONNECT BY child_department = PRIOR parent_department
START WITH child_department = v_department
)
WHERE lvl = 9;
See Oracle docs on hierarchical queries
This is a large pl/sql procedure that i wrote a long while ago that was meant to traverse a employee/boss reporting tree all the way to the top (CEO). This version was specific to Peoplesoft but it as long as your reading something that has a parent/child relationship in a record it will work on anything.... I have other more dynamic versions of this but this maybe the simplest to decipher. I removed some fluff stuff that you won't care about. Also this particular solution delivers to a table for many different reasons because reporting tools can consume it...
Also it determines levels dynamically so you don't have to know how many levels there are as you would with a connect by solution.
Hope it helps:
CREATE OR REPLACE PROCEDURE DW."SPW_T_RESOURCE_HIERARCHY" (iCommit In Integer Default 1000,
pdBegin In Date Default trunc(Sysdate) - 3,
pdEnd In Date Default trunc(Sysdate) + 1,
vTruncate In Varchar2 Default 'Y' ) Is
------------------------------------------------------
-- DECLARATIONS
------------------------------------------------------
Cursor curDataSource Is
---**********************************----
---****BEGIN CUSTOMIZE THIS BLOCK****----
---**********************************----
Select
Eh.empl_id F_DESCENDANT_ID
,Eh.Super_Empl_Id F_IMMEDIATE_ANCESTOR_ID
From
Employee_Header EH
Where
EH.SUPER_EMPL_ID IS NOT NULL OR EH.TERM_DATE IS NULL;
---**********************************----
---****END CUSTOMIZE THIS BLOCK******----
---**********************************----
dNow Date := Sysdate;
iTotalRows Integer := 0;
iTotalErrors Integer := 0;
---**********************************----
---****BEGIN CUSTOMIZE THIS BLOCK****----
---**********************************----
vDescendentID Varchar2(20);
iDescendentLevel Integer := 1;
iAncestorLevel Integer := 0;
vAncestorID Varchar2(20);
vTmpAncestorID Varchar2(20);
vTmpEmployeeID Varchar2(20);
---**********************************----
---****END CUSTOMIZE THIS BLOCK******----
---**********************************----
------------------------------------------------------
-- END DECLARATIONS
------------------------------------------------------
------------------------------------------------------
-- BEGIN MAIN
------------------------------------------------------
Begin
-- Loop over source records
For recDataSource In curDataSource
Loop
iDescendentLevel := 1;
vAncestorID := recDataSource.f_Immediate_Ancestor_Id;
-- Start Transaction
Begin
while (Trim(vAncestorID) is not null)
loop
Begin
-- Fetch Next Ancestor
Select EH.SUPER_EMPL_ID
Into vTmpAncestorID
From
EMPLOYEE_HEADER EH
Where
EH.EMPL_ID = vAncestorID;
Exception
When Others Then
vTmpAncestorID := null;
End;
If NVL(vTmpAncestorID,'-XYZ-') = NVL(vAncestorID,'-123-') Then
vTmpAncestorID := null;
End If;
vAncestorID := vTmpAncestorID;
iDescendentLevel := iDescendentLevel + 1;
end loop;
-- Insert Resource Base
Insert Into T_RESOURCE_HIERARCHY
(
T_RESOURCE_HIERARCHY.F_HIERARCHY_NAME,
T_RESOURCE_HIERARCHY.F_DESCENDANT_LEVEL,
T_RESOURCE_HIERARCHY.F_DESCENDANT_ID,
T_RESOURCE_HIERARCHY.F_ANCESTOR_LEVEL,
T_RESOURCE_HIERARCHY.F_ANCESTOR_ID
)
Values
(
'Physical Org Chart',
iDescendentLevel,
recDataSource.f_Descendant_Id,
To_Number(Decode(iDescendentLevel,1,2,iDescendentLevel) - 1),
NVL(recDataSource.f_Immediate_Ancestor_Id,'ROOT')
);
-- Insert MySelf Into Resource Base as well for full hierarchy research
Insert Into T_RESOURCE_HIERARCHY
(
T_RESOURCE_HIERARCHY.F_HIERARCHY_NAME,
T_RESOURCE_HIERARCHY.F_DESCENDANT_LEVEL,
T_RESOURCE_HIERARCHY.F_DESCENDANT_ID,
T_RESOURCE_HIERARCHY.F_ANCESTOR_LEVEL,
T_RESOURCE_HIERARCHY.F_ANCESTOR_ID
)
Values
(
'Physical Org Chart',
iDescendentLevel,
recDataSource.f_Descendant_Id,
iDescendentLevel,
NVL(recDataSource.f_Descendant_Id,'ROOT')
);
-- Now Its Time To Climb The Tree
-- For This Employee
vAncestorID := recDataSource.f_Immediate_Ancestor_Id;
iAncestorLevel := iDescendentLevel-1;
vTmpAncestorID := null;
-- Loop over parents
while (Trim(vAncestorID) is not null)
loop
Begin
-- Fetch Next Ancestor
Select EH.SUPER_EMPL_ID
Into vTmpAncestorID
From
EMPLOYEE_HEADER EH
Where
EH.EMPL_ID = vAncestorID;
Exception
When Others Then
vTmpAncestorID := null;
End;
If NVL(vTmpAncestorID,'-XYZ-') = '-XYZ-' Then
vTmpAncestorID := null;
End If;
vAncestorID := vTmpAncestorID;
iAncestorLevel := iAncestorLevel - 1;
If vAncestorID is not null Then
-- Insert Resource Base
Insert Into T_RESOURCE_HIERARCHY
(
T_RESOURCE_HIERARCHY.F_HIERARCHY_NAME,
T_RESOURCE_HIERARCHY.F_DESCENDANT_LEVEL,
T_RESOURCE_HIERARCHY.F_DESCENDANT_ID,
T_RESOURCE_HIERARCHY.F_ANCESTOR_LEVEL,
T_RESOURCE_HIERARCHY.F_ANCESTOR_ID
)
Values
(
'Physical Org Chart',
iDescendentLevel,
recDataSource.f_Descendant_Id,
iAncestorLevel,
vAncestorID
);
End If;
end loop;
-- TRANSACTION EXCEPTION HANDLING
Exception
When Others Then
End;
-- ASSIGN HOW MANY RECORDS PROCESSED
iTotalRows := curDataSource%Rowcount;
-- CONDITIONAL/INCREMENTAL TRANSACTION COMMIT
If Mod(iTotalRows, iCommit) = 0
Then
Commit;
End If;
End Loop;
-- FINAL COMMIT AND MD UPDATE
Commit;
-- MAIN EXCEPTION HANDLING
Exception
When Others Then
Begin
iExceptionCode := Sqlcode;
vExceptionMessage := Sqlerrm;
Raise_application_error(Sqlcode, Sqlerrm);
End;
------------------------------------------------------
-- END MAIN
------------------------------------------------------
End SPW_T_RESOURCE_HIERARCHY;
/
Please, check the following example. Not tested, but believe :)
DECLARE
G_EMPLOYEE_ID NUMBER:=1880;
FUNCTION GET_MANAGER(V_EMPLOYEE_ID NUMBER) RETURN NUMBER IS
V_MANAGER_ID NUMBER;
BEGIN
SELECT ID_MANAGER INTO V_MANAGER_ID FROM EMPLOYEES WHERE EMPLOYEE_ID = V_EMPLOYEE_ID;
RETURN V_MANAGER_ID;
EXCEPTION
WHEN OTHERS THEN
RETURN NULL;
END;
BEGIN
LOOP
DBMS_OUTPUT.PUT_LINE('EMPLOYEE:' || G_EMPLOYEE_ID);
G_EMPLOYEE_ID := GET_MANAGER(G_EMPLOYEE_ID);
DBMS_OUTPUT.PUT_LINE('MANAGER:' || G_EMPLOYEE_ID);
EXIT WHEN G_EMPLOYEE_ID IS NULL;
END LOOP;
END;
Another great option (primary) is CONNECT BY, START WITH
I have a procedure that performs some calculations on all records returned by a cursor. It looks a bit like this:
PROCEDURE do_calc(id table.id_column%TYPE)
IS
CURSOR c IS
SELECT col1, col2, col3
FROM table
WHERE ...;
BEGIN
FOR r IN c LOOP
-- do some complicated calculations using r.col1, r.col2, r.col3 etc.
END LOOP;
END;
Now I have the case where I need to perform the exact same calculation on a different set of records that come from a different table. However, these have the same "shape" as in the above in example.
Is it possible to write a procedure that looks like this:
PROCEDURE do_calc2(c some_cursor_type)
IS
BEGIN
FOR r IN c LOOP
-- do the calc, knowing we have r.col1, r.col2, r.col3, etc.
END LOOP;
END;
I know about SYS_REFCURSOR, but I was wondering if it was possible to use the much more convenient FOR ... LOOP syntax and implicit record type.
Create a package.
Declare your cursor as package variable.
Use %rowtype to set function parameter type.
create or replace package test is
cursor c is select 1 as one, 2 as two from dual;
procedure test1;
function test2(test_record c%ROWTYPE) return number;
end test;
create or replace package body test is
procedure test1 is
begin
for r in c loop
dbms_output.put_line(test2(r));
end loop;
end;
function test2(test_record c%ROWTYPE) return number is
l_summ number;
begin
l_summ := test_record.one + test_record.two;
return l_summ;
end;
end test;
I had a similar problem, where I had two cursors that needed to be processed the same way, so this is how I figured it out.
DECLARE
--Define our own rowType
TYPE employeeRowType IS RECORD (
f_name VARCHAR2(30),
l_name VARCHAR2(30));
--Define our ref cursor type
--If we didn't need our own rowType, we could have this: RETURN employees%ROWTYPE
TYPE empcurtyp IS REF CURSOR RETURN employeeRowType;
--Processes the cursors
PROCEDURE process_emp_cv (emp_cv IN empcurtyp) IS
person employeeRowType;
BEGIN
LOOP
FETCH emp_cv INTO person;
EXIT WHEN emp_cv%NOTFOUND;
DBMS_OUTPUT.PUT_LINE('Name = ' || person.f_name ||
' ' || person.l_name);
END LOOP;
END;
--Defines the cursors
PROCEDURE mainProcedure IS
emp empcurtyp;
BEGIN
OPEN emp FOR SELECT first_name, last_name FROM employees WHERE salary > 50000;
process_emp_cv(emp);
CLOSE emp;
OPEN emp FOR SELECT first_name, last_name FROM kuren WHERE first_name LIKE 'J%';
process_emp_cv(emp);
CLOSE emp;
END;
BEGIN
mainProcedure;
END;
/
You can also use this if you want to bulk collect your cursors. You just need to change your helper procedure process_emp_cv; the rest can stay the same.
Using BULK COLLECT
--Processes the cursors
PROCEDURE process_emp_cv (emp_cv IN empcurtyp) IS
TYPE t_employeeRowTable IS TABLE OF employeeRowType;
employeeTable t_employeeRowTable;
BEGIN
LOOP
FETCH emp_cv BULK COLLECT INTO employeeTable LIMIT 50;
FOR indx IN 1 .. employeeTable.Count
LOOP
DBMS_OUTPUT.PUT_LINE('Name = ' || employeeTable(indx).f_name ||
' ' || employeeTable(indx).l_name);
END LOOP;
EXIT WHEN emp_cv%NOTFOUND;
END LOOP;
END;
Try this one, Usong ref cursor.
declare
type c is ref cursor;
c2 c;
type rec is record(
id number,
name varchar(20)
);
r rec;
procedure p1(c1 in out c,r1 in out rec)is begin
loop
fetch c1 into r1;
exit when c1%notfound;
dbms_output.put_line(r1.id || ' ' ||r1.name);
end loop;
end;
begin
open c2 for select id, name from student;
p1(c2,r);
end;
Yes you can use Cursor explicitly into procedure and function,for that cursor need to declare into package as variable