Procedure to update column values in Oracle - oracle

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.)

Related

why if block not working within a procedure while comparing two columns of two tables

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;
/

Retrieve all data from salary table and also calculate Gross Salary(Basic+HRA+DA+CA+Medical) using Stored procedure? in Oracle

I have created Procedure which calculates gross salary of all employees from salary table.When executing stored procedure using execute statement Error:"invalid SQL statement" occurs and when i am executing procedure using PLSQL block then Error":PLS-00905 Object HR.PROC_GROSSSALARY is invalid" occurs
-- Creating Stored procedure --
create or replace procedure proc_grosssalary
AS
begin
select s.*,(Basic+HRA+DA+CA+Medical) Gross_Salary from salary s;
end;
-- Calling SP using EXECUTE --
execute proc_grosssalary;
-- Calling SP using PLSQL Block --
begin
proc_grosssalary;
end;
display all data in salary table with calculated Gross_Salary in the form of table structure
your PL/SQL syntax is not correct
create or replace procedure proc_grosssalary (out gross_salary number)
AS
begin
select (Basic+HRA+DA+CA+Medical)
into gross_salary
from salary s;
end;
You should also consider the option of creating a View.
Create or replace view v_grosssalary
as select s.*,(Basic+HRA+DA+CA+Medical) Gross_Salary
from salary s;
and simply do select * from v_grosssalary to get the output.
With procedures, there's also a PRINT command(which works in SQL* Plus and SQL developer) using CURSOR bind variables
VARIABLE x REFCURSOR
create or replace procedure proc_grosssalary
AS
begin
OPEN cursor :x for
select s.*,(Basic+HRA+DA+CA+Medical) Gross_Salary from salary s;
end;
/
Print x -- This will display the required result.
But.. What do you want to do? If you want calculate salary for every employee and you have your information in the same row, with a simple SQL you could have it, something like this:
Select id_emp, name_emp, surname_emp, (Basic+HRAs+DA+CA+Medical) as salary
from salary;
And it would be more quick than have strange functions and procedures.. If you don't like it, create a view like these and you'll have a column with the salary calculated, more clean and less obscure
You need to use a cursor so that you can loop on records in the table.
Here is what I did -
#create a table for sample data
create table tsalary0918(Basic number,HRA number,DA number,CA number,Medical number)
#inserting sample data
insert into tsalary0918 values (100,100,100,100,100)
#checking if data is inserted
select * from tsalary0918;
#query output
BASIC HRA DA CA MEDICAL
100 100 100 100 100
#create a stored procedure
create or replace procedure testproc
AS
cursor cur is select s.*,(Basic +HRA + DA +CA +Medical ) Gross_salary from tsalary0918 s;
begin
for records in cur
loop
dbms_output.put_line('DA is ' || records.DA);
dbms_output.put_line('Gross Salary is ' || records.Gross_salary);
end loop;
end;
/
#execute the stored procedure
begin
testproc;
end;
/
#output of the above execution
dbms_output:
DA is 100
Gross Salary is 500
Check online - https://dbfiddle.uk/?rdbms=oracle_11.2&fiddle=1744281398fe62a6cb42250641947249
Task completed:
create or replace procedure p1
IS
cursor c1 is select s.*,(Basic+HRA+CA+DA+Medical) Gross_Salary from salary s;
emp_rec c1%rowtype;
begin
open c1;
dbms_output.put_line('Emp_No Basic HRA Gross_Salary');
dbms_output.put_line('---------------------------------------');
loop
fetch c1 into emp_rec;
exit when c1%notfound;
dbms_output.put_line(emp_rec.employee_number||' '||emp_rec.Basic||' '||emp_rec.HRA||' '||' '||emp_rec.Gross_Salary);
end loop;
close c1;
end;
-- Executing the procedure using PLSQL block --
begin
p1;
end;
Below is expected Final result:
----------------------------------------------
Employee_number Basic HRA Gross_salary
----------------------------------------------
1 25000 10000 36750
2 7000 2800 11650
3 10000 4000 15950

Multiple line output in pl/sql

I have a PL/SQL file that has a loop structure.
The script is as follows.
SET SERVEROUTPUT ON
declare
c_id employee.id%type;
c_name employee.name%type;
c_address employee.address%type;
CURSOR c_employee is
SELECT id, name, address from employee;
begin
open c_employee;
LOOP
FETCH c_employee into c_id, c_name, c_address;
EXIT when c_employee%notfound;
dbms_output.put_line(c_id||' '||c_name||' '||c_address);
END LOOP;
close c_employee;
end;
/
When I run this from SQLPlus I get only the details of the first row but not the rest. What am I doing wrong? How to get all the outputs for the loop.
Try to convert your code to use a for loop instead of the open statement, like so -
for r_employee in c_employee
LOOP
dbms_output.put_line(r_employee.c_id||' '||r_employee.c_name||' '||r_employee.c_address);
END LOOP;
Where r_employee is a variable of employee%type.
The way you currently wrote it does not iterate through the cursor, and this is why only the first row is presented.
Even though your code looks correct, it should iterate through all the
row not just one. Try to use below snippet and run it in SQL plus if
still single row then there may be some other issue.
SET SERVEROUTPUT ON
DECLARE
BEGIN
FOR I IN
(SELECT id, name, address FROM employee
)
LOOP
dbms_output.put_line(I.ID||' '||I.name||' '||I.address);
END LOOP;
END;
/

how to generate a table of random data from existing database table through oracle procedure

I have to generate a table (contains two columns) of random data from a database table through oracle procedure. The user can indicate the number of data required and we have to use the table data with ID values from 1001 to 1060. I am trying to use cursor loop and not sure dbms_random method dhould I use.
I am using the following code to create procedure
create or replace procedure a05_random_plant(p_count in number)
as
v_count number := p_count;
cursor c is
select plant_id, common_name
from ppl_plants
where rownum = v_count
order by dbms_random.value;
begin
delete from a05_random_plants_table;
for c_table in c
loop
insert into a05_random_plants_table(plant_id, plant_name)
values (c_table.plant_id, c_table.common_name);
end loop;
end;
/
it complied successfully. Then I executed with the following code
set serveroutput on
exec a05_random_plant(5);
it shows anonymous block completed
but when run the following code, I do not get any records
select * from a05_random_plants_table;
The rownum=value would not work for a value greater than 1
hence try the below
create or replace procedure a05_random_plant(p_count in number)
as
v_count number := p_count;
cursor c is
select plant_id, common_name
from ppl_plants
where rownum <= v_count
order by dbms_random.value;
begin
delete from a05_random_plants_table;
for c_table in c
loop
insert into a05_random_plants_table(plant_id, plant_name)
values (c_table.plant_id, c_table.common_name);
end loop;
end;
/
Query by Tom Kyte - will generate almost 75K of rows:
select trunc(sysdate,'year')+mod(rownum,365) TRANS_DATE,
mod(rownum,100) CUST_ID,
abs(dbms_random.random)/100 SALES_AMOUNT
from all_objects
/
You can use this example to write your query and add where clause to it - where id between 1001 and 1060, for example.
I don't think you should use a cursor (which is slow naturally) but do a direct insert from a select:
insert into table (col1, col2)
select colx, coly from other_table...
And, isn't missing a COMMIT on the end of your procedure?
So, all code in your procedure would be a DELETE, a INSERT WITH that SELECT and then a COMMIT.

oracle stored procedure - select, update and return a random set of rows

oracle i wish to select few rows at random from a table, update a column in those rows and return them using stored procedure
PROCEDURE getrows(box IN VARCHAR2, row_no IN NUMBER, work_dtls_out OUT dtls_cursor) AS
v_id VARCHAR2(20);
v_workname VARCHAR2(20);
v_status VARCHAR2(20);
v_work_dtls_cursor dtls_cursor;
BEGIN
OPEN v_work_dtls_cursor FOR
SELECT id, workname, status
FROM item
WHERE status IS NULL
AND rownum <= row_no
FOR UPDATE;
LOOP
FETCH v_work_dtls_cursor
INTO v_id ,v_workname,v_status;
UPDATE item
SET status = 'started'
WHERE id=v_id;
EXIT
WHEN v_work_dtls_cursor % NOTFOUND;
END LOOP;
close v_work_dtls_cursor ;
/* I HAVE TO RETURN THE SAME ROWS WHICH I UPDATED NOW.
SINCE CURSOR IS LOOPED THRU, I CANT DO IT. */
END getrows;
PLEASE HELP
Following up on Sjuul Janssen's excellent recommendation:
create type get_rows_row_type as object
(id [item.id%type],
workname [item.workname%type],
status [item.status%type]
)
/
create type get_rows_tab_type as table of get_rows_row_type
/
create function get_rows (box in varchar2, row_no in number)
return get_rows_tab_type pipelined
as
v_work_dtls_cursor dtls_cursor;
l_out_rec get_rows_row_type;
BEGIN
OPEN v_work_dtls_cursor FOR
SELECT id, workname, status
FROM item sample ([ROW SAMPLE PERCENTAGE])
WHERE status IS NULL
AND rownum <= row_no
FOR UPDATE;
LOOP
FETCH v_work_dtls_cursor
INTO l_out_rec.id, l_out_rec.workname, l_outrec.status;
EXIT WHEN v_work_dtls_cursor%NOTFOUND;
UPDATE item
SET status = 'started'
WHERE id=l_out_rec.id;
l_out_rec.id.status := 'started';
PIPE ROW (l_out_rec);
END LOOP;
close v_work_dtls_cursor ;
END;
/
A few notes:
This is untested.
You'll need to replace the bracketed section in the type declarations with appropriate types for your schema.
You'll need to come up with an appropriate value in the SAMPLE clause of the SELECT statement; it might be possible to pass that in as an argument, but that may require using dynamic SQL. However, if your requirement is to get random rows from the table -- which just filtering by ROWNUM will not accomplish -- you'll want to do something like this.
Because you're SELECTing FOR UPDATE, one session can block another. If you're in 11g, you may wish to examine the SKIP LOCKED clause of the SELECT statement, which will enable multiple concurrent sessions to run code like this.
Not sure where you are doing your committing, but based on the code as it stands all you should need to do is SELECT ... FROM ITEM WHERE STATUS='started'
If it is small numbers, you could keep a collection of ROWIDs.
if it is larger, then I'd do an
INSERT into a global temporary table SELECT id FROM item .. AND ROWNUM < n;
UPDATE item SET status = .. WHERE id in (SELECT id FROM global_temp_table);
Then return a cursor of
SELECT ... FROM item WHERE id in (SELECT id FROM global_temp_table);
Maybe this can help you to do what you want?
http://it.toolbox.com/blogs/database-solutions/returning-rows-through-a-table-function-in-oracle-7802
A possible solution:
create type nt_number as table of number;
PROCEDURE getrows(box IN VARCHAR2,
row_no IN NUMBER,
work_dtls_out OUT dtls_cursor) AS
v_item_rows nt_number;
indx number;
cursor cur_work_dtls_cursor is
SELECT id
FROM item
WHERE status IS NULL
AND rownum <= row_no
FOR UPDATE;
BEGIN
open cur_work_dtls_cursor;
fetch cur_work_dtls_cursor bulk collect into nt_number;
for indx in 1 .. item_rows.count loop
UPDATE item
SET status = 'started'
WHERE id=v_item_rows(indx);
END LOOP;
close cur_work_dtls_cursor;
open work_dtls_out for select id, workname, status
from item i, table(v_item_rows) t
where i.id = t.column_value;
END getrows;
If the number of rows is particularly large, the global temporary solution may be better.

Resources