I got two tables (ORACLE): D_CONTROL_CARGA and F_PS_CARGA_DIARIA_D
SELECT control_id, control_cd, control_query_tx
FROM ONHR_DIM.D_CONTROL_CARGA;
SELECT FECHA_ID, CONTROL_ID, CONTROL_CD, CANTIDAD_CA
FROM onhr_dim.F_PS_CARGA_DIARIA_D;
I want to update the column CANTIDAD_CA with this PL/SQL code:
DECLARE
sql_qry ONHR_DIM.D_CONTROL_CARGA.control_query_tx%TYPE;
cantidad_ca ONHR_DIM.F_PS_CARGA_DIARIA_D.CANTIDAD_CA%TYPE;
CURSOR cQueries IS SELECT control_id,control_query_tx FROM ONHR_DIM.D_CONTROL_CARGA;
BEGIN
FOR reg IN cQueries LOOP
sql_qry:=SUBSTR(reg.control_query_tx, 0, LENGTH(reg.control_query_tx) - 1);
IF sql_qry not like '%to_char(sysdate%' THEN
execute immediate sql_qry into cantidad_ca;
UPDATE ONHR_DIM.F_PS_CARGA_DIARIA_D SET CANTIDAD_CA=cantidad_ca WHERE CONTROL_ID=reg.control_id AND FECHA_ID=TRUNC(SYSDATE);
END IF;
END LOOP;
END;
Then I got: Updated Rows -1
What Do I have to change?
Related
I need to check whether std_id is present same as in students table if this matches with std_id in std_grace_marks then I need to add that grace_marks of that std_id with marks column in students table.
I have created a procedure and created two cursors to fetch records in loop and I wrote if condition to check whether std_id matches if it is then I am adding marks with grace_marks but if condition is not working here... so please can anyone tell where I am going wrong
My code:
create or replace procedure std_info
IS
CURSOR stdcur IS SELECT std_id,std_name, marks,mark_status FROM students;
CURSOR gracer IS SELECT std_id,grace_marks from student_grace_marks;
myvar stdcur%ROWTYPE;
mycur gracer%ROWTYPE;
BEGIN
OPEN stdcur;
OPEN gracer;
LOOP
FETCH stdcur INTO myvar;
FETCH gracer INTO mycur;
EXIT WHEN stdcur%NOTFOUND;
DBMS_OUTPUT.PUT_LINE( myvar.std_id || ' '|| myvar.std_name||' '||myvar.marks );
if(myvar.std_id=mycur.std_id) then
update students set marks=myvar.marks+mycur.grace_marks;
end if;
END LOOP;
CLOSE stdcur;
CLOSE gracer;
END;
I'm not sure this will ever work. You're fetching a row of each resultset in a row variable, assuming that row 1 for the first resultset will match the std_id of row 1 for the 2nd resultset. There are 2 things wrong with this assumption
There is no ORDER BY clause in the statements, so std_id for the firs row of cursor stdcur could be 10 while std_id for the first row of cursor gracer could be 99. Oracle does not guarantee the order of the a resultset unless an ORDER BY clause is included in the statement.
If table student_grace_marks has more than 1 row, or no rows for a std_id, it will start failing from that row onwards since the count will no longer match.
One solution is to use explicit cursor for loops:
DECLARE
BEGIN
FOR r_student IN (SELECT * FROM students) LOOP
FOR r_student_gm IN (SELECT * FROM student_grace_marks WHERE std_id = r_student.std_id) LOOP
UPDATE students SET marks = marks + r_student_gm.grace_marks WHERE std_id = r_student.std_id;
END LOOP;
END LOOP;
END;
/
.. or .. if you want to use explicit cursors:
In this case the CURSOR gracer will have a where clause to only select the relevant row(s) for that particular student.
Note that I fixed some errors as well
DECLARE
--declare variable to be used in the where clause of the select for cursor gracer.
l_std_id students.std_id%TYPE;
CURSOR stdcur IS SELECT std_id,std_name, marks FROM students;
CURSOR gracer IS SELECT std_id,grace_marks from student_grace_marks WHERE std_id = l_std_id;
myvar stdcur%ROWTYPE;
mycur gracer%ROWTYPE;
BEGIN
OPEN stdcur;
LOOP
FETCH stdcur INTO myvar;
EXIT WHEN stdcur%NOTFOUND;
l_std_id := myvar.std_id;
OPEN gracer;
LOOP
FETCH gracer INTO mycur;
EXIT WHEN gracer%NOTFOUND;
-- use marks, not myvar.marks. If there is >1 record in student_grace_marks it will only add the last value.
-- add the where clause or every update will update all rows and every grace_marks will be added for every student in the table.
UPDATE students set marks= marks+mycur.grace_marks WHERE std_id = l_std_id;
END LOOP;
CLOSE gracer;
END LOOP;
CLOSE stdcur;
END;
/
So,I am working with a cursor,the cursor was initially operating with the initial for loop,now i needed to perform operation if the no of records in the cursor is more than one,so i fetched the no of records first and stored in a variable and used an if-condition based on that.Now the problem is when I run the whole process,the procedure does its job,but only for the first record in the cursor and entirely skips the second record.Please suggest or help me identify the mistake.
Adding code snippet.
for m in get_m_p(a,b)--main cursor
loop
fetch get_m_p into c_m;
exit when g_m_p%notfound;
end loop;
tempcount := g_m_p%ROWCOUNT:
statements---
if(tempcount>1) then
statements----
end if;
end loop;
for the two records the main curosr is returning in first line,operations are only done for the first one,and the second record is being skipped entirely.
This is a superfluous line:
fetch get_m_p into c_m;
You don't explicitly fetch within a cursor FOR loop, it is implicitly done in each loop iteration. Remove that line.
How to get number of rows returned by a cursor? Lucky you, it seems that you don't care whether how many rows - exactly - it returns. All you want to know is whether it returned more than 1 row. So, count them, literally; exit the loop if counter exceeds 1.
SQL> DECLARE
2 CURSOR get_m_p IS
3 SELECT *
4 FROM emp
5 WHERE deptno = 10;
6
7 l_cnt NUMBER := 0;
8 BEGIN
9 FOR m IN get_m_p
10 LOOP
11 l_cnt := l_cnt + 1;
12 EXIT WHEN l_cnt > 1;
13 END LOOP;
14
15 DBMS_OUTPUT.put_line ('Cursor returned at least ' || l_cnt || ' row(s)');
16
17 IF l_cnt > 1
18 THEN
19 NULL;
20 -- the rest of statements go here
21 END IF;
22 END;
23 /
Cursor returned at least 2 row(s)
PL/SQL procedure successfully completed.
SQL>
As there's no way to know how many rows will cursor return, unfortunately, you'll have to check that first, and then decide what to do with the result.
DECLARE
CURSOR get_m_p IS
SELECT *
FROM emp
WHERE deptno = 10;
l_cnt NUMBER := 0;
BEGIN
SELECT COUNT (*)
INTO l_cnt
FROM (-- this is cursor's SELECT statement
SELECT *
FROM emp
WHERE deptno = 10);
FOR m IN get_m_p
LOOP
-- some statements here
IF l_cnt > 1
THEN
NULL;
-- statements to be executed if cursor return more than 1 row
END IF;
END LOOP;
END;
/
Cursor:
Oracle creates memory area to process SQL statement which is called context area and the cursor is pointer to the context area. A cursor holds the rows (one or more) returned by a SQL statement. The set of rows the cursor holds is referred to as the active set.
There are two type of cursor
1. Implicit cursor
2. Explicit cursor
Implicit Cursors :
Implicit cursors are automatically created by Oracle whenever an SQL statement is executed. Any SQL cursor attribute will be accessed as sql%attribute_name as shown below in the example. Use the SQL%ROWCOUNT attribute to determine the number of rows affected
DECLARE
no_of_records number(2);
BEGIN
select * from records;
IF sql%notfound THEN
dbms_output.put_line('no records present');
ELSIF sql%found THEN
no_of_records := sql%rowcount;
IF no_of_records > 1 THEN
dbms_output.put_line('no of records ' || no_of_records);
END IF
END IF;
END;
Explicit Cursors :
Explicit cursors are programmer-defined cursors for gaining more control over the context area. An explicit cursor should be defined in the declaration section of the PL/SQL Block. It is created on a SELECT Statement which returns more than one row.
Please see below example:
DECLARE
r_id records.id%type;
CURSOR c_records is
SELECT id FROM records;
BEGIN
OPEN c_records;
LOOP
FETCH c_records into r_id;
EXIT WHEN c_records%notfound;
dbms_output.put_line('Record id ' || r_id );
END LOOP;
CLOSE c_records;
END;
Reference :
https://www.tutorialspoint.com/plsql/plsql_cursors.htm
As an alternative you can cache every row and process after.
Example using sample schema "HR" on Oracle 11g Express Edition:
DECLARE
CURSOR get_m_p
IS
SELECT *
FROM hr.employees
WHERE department_id = 60
order by employee_id;
--
rcEmp_last get_m_p%rowtype;
l_cnt NUMBER;
BEGIN
FOR rcM IN get_m_p LOOP
l_cnt := get_m_p%rowcount;
Dbms_Output.Put_Line('l_cnt='||l_cnt);
if l_cnt=1 then
rcEmp_last:=rcM;
Else
Dbms_Output.Put_Line('Process='||to_char(l_cnt-1));
Dbms_Output.Put_Line('rcEmp_last.employee_id='||rcEmp_last.employee_id);
--
rcEmp_last:=rcM;
END IF;
End loop;
--
Dbms_Output.Put_Line('Exited FOR-LOOP');
Dbms_Output.Put_Line('l_cnt='||l_cnt);
--
if l_cnt>1 then
Dbms_Output.Put_Line('rcEmp_last.employee_id='||rcEmp_last.employee_id);
End if;
END;
Output:
Text
PL/SQL block, executed in 1 ms
l_cnt=1
l_cnt=2
Process=1
rcEmp_last.employee_id=103
l_cnt=3
Process=2
rcEmp_last.employee_id=104
l_cnt=4
Process=3
rcEmp_last.employee_id=105
l_cnt=5
Process=4
rcEmp_last.employee_id=106
Exited FOR-LOOP
l_cnt=5
rcEmp_last.employee_id=107
Total execution time 35 ms
I have a procedure in oracle that looks like this
create or replace procedure check_display_stock as
id_brg number(20);
rowcount number (20);
begin
-- 1. insert datas from display into temp_display
insert into temp_display
select id_barang,stok,min_stok
from display
where stok <= min_stok;
--2. select the number of datas in temp_display
select count(rownum)
into rowcount
from temp_display;
while(rowcount != 0)
loop
-- Error: no data found
select id_barang
into id_brg
from temp_display
where rownum = 1;
--just another procedure to do other things
insert_spb(id_brg);
delete from temp_display where rownum = 1;
end if;
end loop;
end check_display_stock;
An error occurs when I tried to select into that says no data found.
I don't understand why this happened.
You never decrement rowcount so you will end up deleting the rows in temp_display one-by-one and then keep going (potentially forever) and on the next iteration after you have already emptied the table you will try to select id_barang into id_brg ... and it will fail as you have already emptied the table.
Instead, you could use BULK COLLECT INTO or a CURSOR to bypass the temporary table:
create or replace procedure check_display_stock
as
begin
FOR cur IN ( select id_barang,
stok,
min_stok
from display
where stok <= min_stok
)
LOOP
insert_spb(cur.id_barang);
END LOOP;
END;
/
db<>fiddle here
Just wondering how I go about returning multiple columns from the database with this stored proc, Thanks.
is
cursor sample_cur is --this can be your select statement
select name as today from names;
begin
for rec in sample_cur loop
-- step by step for each record you return in your cursor
dbms_output.put_line(rec.name);
end loop;
end;
Cursor can return multiple columns, for example:
procedure list_something(p_result out sys_refcursor) as
begin
open p_result for
select t.column1,
t.column2
from MY_TABLE t
where t.column3 is not null;
end;
Next you can iterate thought this cursor from Java/.Net, etc.
Apart from Manushin's answer, If you strictly wants answer in your format, You may try below -
is
cursor sample_cur is --this can be your select statement
select name, other_column1, other_column2 as today from names;
begin
for rec in sample_cur loop
-- step by step for each record you return in your cursor
dbms_output.put_line(rec.name || rec.other_column1 || rec.other_column2);
end loop;
end;
With a procedure in Oracle I want to update the code field of all records in a table from an input parameter. For example, if the parameter value is 100:
row 1 with code 256 change to 101
row 2 with code 368 change to 102
row 3 with code 624 change to 103
I try this
create or replace PROCEDURE procedure_name (parameter_newcode code%TYPE) AS
CURSOR cursor_name IS select code from table
order by code asc;
var_newCode code%TYPE;
BEGIN
OPEN cursor_name;
LOOP
FETCH cursor_name INTO var_newCode;
EXIT WHEN cursor_name%NOTFOUND;
var_newCode:=parameter_newcode+1;
update table set code = var_newCode;
END LOOP;
CLOSE cursor_name;
END procedure_name;
but returns the same code for each row (for example, if the parameter is 100 - > 101)
In each loop you are setting (again and again)
var_newCode:=parameter_newcode+1;
and you are not specifying, which record to update
update table set code = var_newCode;
Something along
create or replace PROCEDURE procedure_name (parameter_newcode code%TYPE) AS
CURSOR cursor_name IS select code from myTable
order by code asc;
var_oldCode code%TYPE;
var_newCode code%TYPE;
BEGIN
var_newCode:=parameter_newcode;
OPEN cursor_name;
LOOP
FETCH cursor_name INTO var_oldCode;
EXIT WHEN cursor_name%NOTFOUND;
var_newCode:=var_newCode+1;
update myTable set code = var_newCode where code = var_oldCode;
END LOOP;
CLOSE cursor_name;
END procedure_name;
should work. (Sorry can't test right now.)