Oracle PL/SQL Procedure with for loop to insert/update - oracle

I need to write PL/SQL procedure with cursor for loop to insert/update the data rowwise.
Data from staging table needs to be populated to main table.
It will first check if the project_id and department exist or not in main table,then it will insert/update accordingly.
(Merge cannot be used as per requirement)
So i have staging table which gets populated.
STAGE_PROJECT
So, if the project_id,department exist,contract and scope columns would get updated.
Else the row would get inserted.
Destination table:
PROJECT_DATA
Example: for ERP and SAP, contract and scope would get updated and for DWH,since the project_id and department do not exists, row will get inserted .

Hopes this helps
I created your tables.
CREATE TABLE STAGE_PROJECT
(
PROJECT_ID NUMBER,
DEPARTMENT VARCHAR2(30),
CONTRACT VARCHAR2(30),
"SCOPE" VARCHAR2(30),
FINAL_DATE DATE
);
CREATE TABLE PROJECT_DATA
(
PROJECT_ID NUMBER,
DEPARTMENT VARCHAR2(30),
CONTRACT VARCHAR2(30),
"SCOPE" VARCHAR2(30),
FINAL_DATE DATE
);
And inserted your data.
INSERT INTO PROJECT_DATA(PROJECT_ID, DEPARTMENT) VALUES (1 , 'ERP');
INSERT INTO PROJECT_DATA(PROJECT_ID, DEPARTMENT) VALUES (2 , 'SAP');
INSERT INTO STAGE_PROJECT(PROJECT_ID, DEPARTMENT, CONTRACT, SCOPE) VALUES (1 , 'ERP', 'NEW','FINAL');
INSERT INTO STAGE_PROJECT(PROJECT_ID, DEPARTMENT, CONTRACT, SCOPE) VALUES (2 , 'SAP', 'OLD','UPCOMING');
INSERT INTO STAGE_PROJECT(PROJECT_ID, DEPARTMENT, CONTRACT, SCOPE) VALUES (3 , 'DWH', 'NEW CONTRA','TARGET');
SELECT * FROM PROJECT_DATA;
SELECT * FROM STAGE_PROJECT;
This PLSQL code loop through your STAGE_PROJECT rows
If rows found in the PROJECT_DATA it will update those rows
otherwise, it will insert the row which is not found.
DECLARE
CURSOR SPCUR IS SELECT * FROM STAGE_PROJECT;
EX PLS_INTEGER;
BEGIN
FOR STAGE_PROJECT_REC IN SPCUR
LOOP
SELECT COUNT(*) INTO EX FROM PROJECT_DATA WHERE
PROJECT_ID = STAGE_PROJECT_REC.PROJECT_ID AND
DEPARTMENT = STAGE_PROJECT_REC.DEPARTMENT;
IF EX > 0 THEN
UPDATE PROJECT_DATA SET CONTRACT = STAGE_PROJECT_REC.CONTRACT,
SCOPE = STAGE_PROJECT_REC.SCOPE,
FINAL_DATE = STAGE_PROJECT_REC.FINAL_DATE
WHERE PROJECT_ID = STAGE_PROJECT_REC.PROJECT_ID AND
DEPARTMENT = STAGE_PROJECT_REC.DEPARTMENT;
ELSE
INSERT INTO PROJECT_DATA(PROJECT_ID, DEPARTMENT, CONTRACT, SCOPE, FINAL_DATE)
VALUES (STAGE_PROJECT_REC.PROJECT_ID, STAGE_PROJECT_REC.DEPARTMENT, STAGE_PROJECT_REC.CONTRACT, STAGE_PROJECT_REC.SCOPE, STAGE_PROJECT_REC.FINAL_DATE);
END IF;
END LOOP;
COMMIT;
END;

Related

What is the right PL/SQL for updating rows without a need to reinsert it?

I new at using PL/SQL and I want the following:
I have this table on Oracle SQLcl
create table Child (
id varchar not null,
name varchar not null,
gender varchar not null,
YearOfBirth number(4) not null,
YearsOfAge number(4) null,
CONSTRAINT Pk primary key (id)
);
And I want a PL/SQL (preferred anonymous) that update field of "YearsOfAge" by minusing 2020 from the "YearOfBirth" field. I could do that but my problem is that the table won't be updated until I insert the PL/SQL block again. So whenever I insert a new row, I have to insert my PL/SQL block again. I want to get the table updated whenever I insert/update a row, without a need to insert this block following a new row.
To be clearer, I just want to insert SL/SQL block one time after creating the table, then get the table's "YearsOfAge" updated whenever I insert/update/delete a row. So when I write "select * from Child;" I need to see the "YearsOfAge" with the new value that computed from subtracting 2020 from "YearOf Birth".
My current PL/SQL is below:
begin
IF INSERTING THEN
update Child set YearsOfAge = 2020 - YearOfBirth;
ELSIF DELETEING THEN
update Child set YearsOfAge = 2020 - YearOfBirth;
ELSE
update Child set YearsOfAge = 2020 - YearOfBirth;
END IF;
END;
/
If you really need to store the age this way, some options are virtual columns, views, and triggers.
Virtual Column
With a virtual column, Oracle will automatically perform the calculation on the fly.
SQL> create table Child
2 (
3 id number not null,
4 name varchar2(10) not null,
5 gender varchar2(10) not null,
6 YearOfBirth number(4) not null,
7 YearsOfAge number generated always as (2020 - yearOfBirth) null,
8 constraint pk_child primary key (id)
9 );
Table created.
SQL> insert into child(id, name, gender, yearOfBirth) values(1, 'A', 'female' , 1990);
1 row created.
SQL> insert into child(id, name, gender, yearOfBirth) values(2, 'B', 'male' , 2000);
1 row created.
SQL> insert into child(id, name, gender, yearOfBirth) values(3, 'C', 'non-binary', 2010);
1 row created.
SQL> select * from child;
ID NAME GENDER YEAROFBIRTH YEARSOFAGE
---------- ---------- ---------- ----------- ----------
1 A female 1990 30
2 B male 2000 20
3 C non-binary 2010 10
View
One downside of virtual columns is that they cannot use functions like SYSDATE, so the year has to be hard-coded. With a view, the expression can reference SYSDATE and will always be up-to-date:
create or replace view child_view as
select id, name, gender, yearOfBirth, extract(year from sysdate) - yearOfBirth yearsOfAge
from child;
Trigger (Warning)
You can also use a trigger to create the value when a row is inserted or updated:
create or replace trigger child_trg
before update or insert on child
for each row
begin
if updating('YEAROFBIRTH') or inserting then
:new.yearsOfAge := extract(year from sysdate) - :new.yearOfBirth;
end if;
end;
/
But in practice, triggers are a pain to maintain. Which leads to the question: why do you want to store this information in the first place?
Good database design should minimize the amount of redundant data. There are always exceptions, but you should have a good reason for those exceptions, like an especially complicated calculation that you don't want others to get wrong, you can't create a PL/SQL function because of an unusual security constraint, etc. Calculating something as trivial as the age may cause more problems than it solves.

oracle trigger after update on specific field followed by insert

I'm creating a trigger that will insert a record into another table based on an after update and certain text being referenced in the update..
CREATE OR REPLACE TRIGGER NCATSPROD_PM.DEACTIVATE_USERS
AFTER UPDATE OF "CHANGEBY_CHAR" ON NCATSPROD_PM.PM_USER_DATA
REFERENCING NEW AS "BICSUPP"
BEGIN
INSERT INTO NCATSPROD_PM.DEACTIVATED_USERS (USERNAME, EMAIL, DEACTIVATED_DATE)
VALUES (SELECT USER_ID, EMAIL, CHANGE_DATE FROM ncatsprod_pm.pm_user_data);
END;
When i try to compile this i'm getting an error
Error(1,3): PL/SQL: SQL Statement ignored
Error(1,90): PL/SQL: ORA-00936: missing expression
A little unsure where i'm going wrong with this? Go to source on the error takes me to the AFTER UPDATE line but looking at examples i don't see what's up. I tried using SQL Developer's trigger wizard and that gave me the same result
The REFERENCING clause is just used to change the names of the built-in OLD and NEW records. OLD contains the values of the row before it was updated, and NEW holds the values after it was updated. Here's an example of how you might use them.
create table PM_USER_DATA (user_id varchar2(20), email varchar2(20), change_date date, changeby_char varchar2(20));
create table DEACTIVATED_USERS (username varchar2(20), email varchar2(20), deactivated_date date);
insert into pm_user_data values ('test', 'test#test.com', sysdate, 'SOMEUSER');
create or replace trigger deactivate_users
after update of changeby_char on pm_user_data
referencing old as o1 new as n1
for each row
begin
if :n1.changeby_char = 'BICSUPP' then
insert into deactivated_users (username, email, deactivated_date)
values (:n1.user_id, :n1.email, :n1.change_date);
end if;
end;
/
update pm_user_data set changeby_char = 'BICSUPP';
select * from deactivated_users;
/* output:
USERNAME EMAIL DEACTIVATED_DATE
-------------------- -------------------- ----------------
test test#test.com 10-OCT-17
1 row selected.
*/

oracle triggers :new context variable

Question:
A Trigger which automatically stores in a separate table called ‘ExcellentSale’ the Sales Agent
name, car model and manufacturer name, each time the agreed price of a
SalesTransaction is more than 20% of the car’s asking price. (Note: You need to create
the ‘ExcellentSale’ table before implementing this trigger. To create the primary key, use a
sequence that starts at 1 and increments by 1).
I am using these tables
Manufacturer(manufacturerID, name, region)
Model(modelNo, name, type, previousModel, manufacturerID)
Car(VIN, dateAcquired, yearBuilt, purchasedPrice, askingPrice,
currentMileage, modelNo)
SalesAgent(agentID, name, DOB)
SalesTransaction(VIN, custID, agentID, dateOfSale, agreedPrice)
Here is my attempt
create sequence generateKey
start with 1
increment by 1;
CREATE TABLE ExcellentSale(
recordNo NUMBER,
agentName VARCHAR2(20) NOT NULL,
modelName VARCHAR2(20) NOT NULL,
manufacturerName VARCHAR2(20) NOT NULL,
PRIMARY KEY(recordNo));
create or replace trigger AutoStore
before insert on SalesTransaction
for each row
declare
SAname varchar2(50);
carModel varchar2(50);
manufacturerName varchar2(50);
askingprice number;
agreedprice number;
begin
select sa.name, mo.name, mu.name, c.askingprice, st.agreedprice
into SAname, CarModel, manufacturerName, askingprice, agreedprice
from manufacturer MU, Model MO, Car C, SalesAgent SA, SalesTransaction ST
where mu.manufacturerid = mo.manufacturerid
and st.vin = c.vin
AND c.vin = :new.vin
AND sa.agentID = :new.agentID;
IF :new.agreedPrice > (1.2 * askingPrice) THEN
INSERT INTO ExcellentSale
VALUES
(generateKey.nextval, agentName, modelName, manufacturerName);
END IF;
end AutoStore;
/
and the error I am getting is
PL/SQL: ORA -00984: Column not allowed here
please help, thank you in advanced.
In the VALUES clause of the insert statement, you have go use the variables that hold the values you need instead of the column names.
INSERT INTO ExcellentSale
VALUES
(generateKey.nextval, SAname, CarModel, manufacturerName);

Using objects to insert variable array into nested table

I have a series of tables and objects I have defined. I have an object nested table that I am trying to insert values into. The values are in the form of a variable array but I don't know how to insert them. my tables and code are as follows.
Table wu.classes
crn number(5)
department varchar2(8)
title carchar2(25)
Table wu.students
student_id char(11)
name varchar2(10)
dept varchar2(8)
advisor varchar(10)
classes wu.classes_va
wu.classes_va varray(5) of number (5)
create type classes_ty as object(crn varchar2(5),department varchar2(8), coursetitle varchar2(25)
create table classes_ot of classes_ty;
insert into classes_ot select crn,department,title from wu.classes;
create or replace type classes_ref_ty as table of ref classes_ty;
create table student_plus(student# varchar2(11),student_name varchar2(10),major varchar2(8), advisor (10), enrolled classes_ref_ty) nested table enrolled store as classes_ref_ty_tab;
Problem here (I need to loop through to fill the table but I just need to know how to do it for one values and i can figure the rest out):
begin
insert into student_plus values('700-123-948','Hooker','CS','VanScoy',classes_ref_ty();
insert into table(select enrolled from student_plus where student#='700-123-948')
select ref(c) from classes_ot c where ???
end;
/
I don't know how to access the variable array and use it with the classes_ref_ty.

Oracle Inserting or Updating a row through a procedure

I have a table
CREATE TABLE STUDENT
(
ID INTEGER PRIMARY KEY,
FIRSTNAME VARCHAR2(1024 CHAR),
LASTNAME VARCHAR2(1024 CHAR),
MODIFIEDDATE DATE DEFAULT sysdate
)
I am inserting a row of data
insert into STUDENT (ID, FIRSTNAME, LASTNAME, MODIFIEDDATE) values (1,'Scott', 'Tiger', sysdate);
When I have to insert a record of data, I need to write a procedure or function which does the following:
if there is no record for the same id insert the row.
if there is a record for the same id and data matches then do nothing.
if there is a record for the same id but data does not match then update the data.
I am new to oracle. From the java end, It is possible to select the record by id and then update that record, but that would make 2 database calls. just to avoid that I am trying update the table using a procedure. If the same can be done in a single database call please mention.
For a single SQL statement solution, you can try to use the MERGE statement, as described in this answer https://stackoverflow.com/a/237328/176569
e.g.
create or replace procedure insert_or_update_student(
p_id number, p_firstname varchar2, p_lastname varchar2
) as
begin
merge into student st using dual on (id = p_id)
when not matched then insert (id, firstname, lastname)
values (p_id, p_firstname, p_lastname)
when matched then update set
firstname = p_firstname, lastname = p_lastname, modifiedate = SYSDATE
end insert_or_update_student;
instead of procedure try using merge in oracle .
If Values is matched it will update the table and if values is not found it will insert the values
MERGE INTO bonuses b
USING (
SELECT employee_id, salary, dept_no
FROM employee
WHERE dept_no =20) e
ON (b.employee_id = e.employee_id)
WHEN MATCHED THEN
UPDATE SET b.bonus = e.salary * 0.1
DELETE WHERE (e.salary < 40000)
WHEN NOT MATCHED THEN
INSERT (b.employee_id, b.bonus)
VALUES (e.employee_id, e.salary * 0.05)
WHERE (e.salary > 40000)
Try this
To solve the second task - "if there is a record for the same id and data matches then do nothing." - starting with 10g we have additional "where" clause in update and insert sections of merge operator.
To do the task we can add some checks for data changes:
when matched then update
set student.last_name = query.last_name
where student.last_name <> query.last_name
This will update only matched rows, and only for rows where data were changed

Resources