Create table statements:
CREATE TABLE Client(
nclient INT NOT NULL,
nom VARCHAR(45) NOT NULL UNIQUE,
plafond NUMERIC(10,2) DEFAULT 0,
CHECK (plafond>=0),
CONSTRAINT PK_Client PRIMARY KEY (nclient)
);
CREATE TABLE Compte(
ncompte INT NOT NULL,
solde NUMERIC(10,2),
ouvert NUMERIC(1) DEFAULT 1,
nbessais INT,
code Numeric(4),
nclient NUMERIC(4),
CONSTRAINT PK_Compte PRIMARY KEY (ncompte),
CONSTRAINT FK_Compte_nclient_Client FOREIGN KEY (nclient) REFERENCES Client(nclient),
CONSTRAINT CK_solde CHECK (solde>=0),
CONSTRAINT CK_ouvert CHECK(ouvert between 0 and 1),
CONSTRAINT CK_code CHECK (code>0)
);
CREATE TABLE Operation(
noperation INT NOT NULL,
dateoperation DATE Default SYSDATE,
codepropose NUMERIC(4),
montant NUMERIC(10,2),
ncompte INT,
CONSTRAINT PK_Operation PRIMARY KEY (noperation),
CONSTRAINT FK_Ope_ncompte_Compte FOREIGN KEY (ncompte) REFERENCES Compte(ncompte),
CONSTRAINT CK_codepropose CHECK (codepropose>=0),
CONSTRAINT CK_montant CHECK (montant>=0)
);
CREATE TABLE Historique(
nhistorique INT NOT NULL,
dateoperation DATE DEFAULT SYSDATE,
montant NUMERIC(10,2),
ncompte INT,
CONSTRAINT PK_Historique PRIMARY KEY (nhistorique)
);
CREATE TABLE Incident(
nincident INT NOT NULL,
message VARCHAR(45),
noperation INT,
CONSTRAINT PK_Incident PRIMARY KEY (nincident),
CONSTRAINT FK_Inc_noperation_Operation FOREIGN KEY (noperation) REFERENCES Operation(noperation)
);
Trigger code:
create or replace TRIGGER TRG_OPERATION_CARTE
AFTER INSERT OR UPDATE ON Operation
FOR EACH ROW
DECLARE
solde NUMBER := 0;
ouvert NUMBER := 0;
essai NUMBER := 0;
code NUMBER := 0;
plafondActuel NUMBER := 0;
decouvertAuth NUMBER := 0;
BEGIN
SELECT SUM(montant) INTO plafondActuel FROM Operation WHERE dateoperation >= SYSDATE - 7 AND ncompte = :NEW.ncompte;
SELECT plafond INTO decouvertAuth FROM Client Cl LEFT JOIN Compte Cp ON Cl.nclient = Cp.nclient WHERE Cp.ncompte = :NEW.ncompte;
SELECT solde, ouvert, nbessais, code
INTO solde, ouvert, essai, code
FROM Compte
WHERE Compte.ncompte = :NEW.ncompte;
IF ouvert = 0 THEN
INSERT INTO Incident (message,noperation) values ('Compte bloqué',:NEW.noperation);
ELSE
IF :NEW.codepropose = code THEN
IF (solde + decouvertAuth) > :NEW.montant THEN
IF (plafondActuel + :NEW.montant) > 300 THEN
INSERT INTO Incident (message,noperation) values ('Lopération dépasse le plafond authorisé',:NEW.noperation);
ELSE
INSERT INTO Historique (montant,ncompte) values (:NEW.montant,:NEW.ncompte);
END IF;
ELSE
INSERT INTO Incident (message,noperation) values ('Lopération dépasse le découvert authorisé',:NEW.noperation);
END IF;
ELSE
essai := essai + 1;
UPDATE Compte SET nbessais = essai WHERE Compte.ncompte = :NEW.ncompte;
IF essai >=3 THEN
UPDATE Compte SET ouvert = 0 WHERE Compte.ncompte = :NEW.ncompte;
INSERT INTO Incident (message,noperation) values ('Compte bloqué, 3 tentatives échouées',:NEW.noperation);
END IF;
END IF;
END IF;
END;
I am pretty sure that the algorithm and the Trigger are correct but I have an error :
Cause: A trigger (or a user defined plsql function that is referenced in this statement) attempted to look at (or modify) a table that was in the middle of being modified by the statement which fired it.
Action: Rewrite the trigger (or function) so it does not read that table.
Here are some tests, everything is ok apart from the last insert :
INSERT INTO Client (nom) values ('abderrahmane');
INSERT INTO Client (nom) values ('ben');
Select * FROM Client;
INSERT INTO Compte (solde,nclient) values (200,1);
INSERT INTO Compte (nclient,code) values (2,2403);
INSERT INTO Compte (solde,nclient) values (2300,3);
SELECT * FROM Compte;
INSERT INTO Operation (montant,ncompte) values (200,2);
INSERT INTO Operation (codepropose,ncompte) values (2010,2);
INSERT INTO Operation (ncompte) values (1);
INSERT INTO Operation (codepropose,montant,ncompte) values (2020,20,1);
INSERT INTO Operation (codepropose,montant,ncompte,dateoperation) values (2030,30,2,'01/01/2001');
INSERT INTO Operation (codepropose,montant,ncompte) values (2030,30,3);
SELECT * FROM Operation;
INSERT INTO Historique (montant,ncompte) values (200,2);
INSERT INTO Historique (dateoperation,ncompte) values ('15/04/16',2);
INSERT INTO Historique (ncompte) values (1);
INSERT INTO Historique (dateoperation,montant,ncompte) values ('15/04/16',20,1);
INSERT INTO Historique (dateoperation,montant,ncompte) values ('15/04/16',30,3);
SELECT * FROM Historique;
INSERT INTO Incident (message,noperation) values ('Incident 1',2);
INSERT INTO Incident (message,noperation) values ('Incident 2',8);
INSERT INTO Incident (message,noperation) values ('Incident 3',1);
SELECT * FROM Incident;
UPDATE Compte SET ouvert = 0 WHERE nclient = 2;
INSERT INTO Operation (montant,ncompte,codepropose) values (200,2,2403);
SELECT * FROM Incident;
Mutating Table error comes because your are using that particular table and making any kind of modification in the same table.
Since you are trying to select records from table in the trigger of the same table you are getting this error.
Thanks everyone for your answers, I just had to change the tablename, instead of taking the sum from operation, I took it from historique :
CREATE OR REPLACE TRIGGER TRG_OPERATION_CARTE
AFTER INSERT OR UPDATE ON Operation
FOR EACH ROW
DECLARE
solde NUMBER := 0;
ouvert NUMBER := 0;
essai NUMBER := 0;
code NUMBER := 0;
plafondActuel NUMBER := 0;
decouvertAuth NUMBER := 0;
BEGIN
SELECT SUM(montant) INTO plafondActuel FROM Historique WHERE dateoperation >= SYSDATE - 7 AND ncompte = :NEW.ncompte;
SELECT plafond INTO decouvertAuth FROM Client Cl LEFT JOIN Compte Cp ON Cl.nclient = Cp.nclient WHERE Cp.ncompte = :NEW.ncompte;
SELECT solde, ouvert, nbessais, code
INTO solde, ouvert, essai, code
FROM Compte
WHERE Compte.ncompte = :NEW.ncompte;
IF ouvert = 0 THEN
INSERT INTO Incident (message,noperation) values ('Compte bloqué',:NEW.noperation);
ELSE
IF :NEW.codepropose = code THEN
IF (solde + decouvertAuth) >= :NEW.montant THEN
IF (plafondActuel + :NEW.montant) > 300 THEN
INSERT INTO Incident (message,noperation) values ('Lopération dépasse le plafond authorisé',:NEW.noperation);
ELSE
UPDATE Compte SET solde = (solde - :NEW.montant) WHERE Compte.ncompte = :NEW.ncompte;
INSERT INTO Historique (montant,ncompte) values (:NEW.montant,:NEW.ncompte);
END IF;
ELSE
INSERT INTO Incident (message,noperation) values ('Lopération dépasse le découvert authorisé',:NEW.noperation);
END IF;
ELSE
essai := essai + 1;
UPDATE Compte SET nbessais = essai WHERE Compte.ncompte = :NEW.ncompte;
IF essai >=3 THEN
UPDATE Compte SET ouvert = 0 WHERE Compte.ncompte = :NEW.ncompte;
INSERT INTO Incident (message,noperation) values (CONCAT('Compte bloqué : tentative n° ', essai),:NEW.noperation);
ELSE
INSERT INTO Incident (message,noperation) values (CONCAT('Code erroné : tentative n° ', essai),:NEW.noperation);
END IF;
END IF;
END IF;
END;
/
Related
I need to insert rows in a Table_A, with values from Table_B on UPDATE of Table_B.
table_A (
station int,
state varchar(20),
CONSTRAINT pk_table_a PRIMARY KEY(station, state),
CONSTRAINT fk_table_A FOREIGN KEY (station, state)
REFERENCES table_B (station, state)
)
table_B (
station int,
state varchar(20),
player int,
date_sent Date DEFAULT NULL
CONSTRAINT pk_table_b PRIMARY KEY(station, state, player)
)
Now, my triggers needs to add a row to table_A (station, state), when all the dates for these (station, state) become NOT NULL in table_B.
Here's my actual which causes mutating table error:
CREATE OR REPLACE TRIGGER add_stations_sent
AFTER INSERT OR UPDATE ON "TABLE_B"
FOR EACH ROW
WHEN (NEW.DATE_SENT IS NOT NULL)
DECLARE
nb_stations_null number;
BEGIN
SELECT COUNT(1)
INTO nbr_stations_null
FROM "TABLE_B"
WHERE "TABLE_B".STATE = :NEW.STATE AND
"TABLE_B".STATION <> :NEW.STATION AND
"TABLE_B".DATE_SENT IS NULL;
IF (nb_stations_null = 0) THEN
INSERT INTO "TABLE_A" VALUES (:NEW.STATION, :NEW.STATE);
END IF;
END;
Something like this will defer the processing to AFTER STATEMENT level and thus allow to you run queries
create or replace
trigger avoid_mutate
for insert or update on table_b
compound trigger
l_station sys.odcivarchar2list := sys.odcivarchar2list();
l_state sys.odcivarchar2list := sys.odcivarchar2list();
before each row is
begin
l_station.extend;
l_state.extend;
l_station(l_station.count) := :new.station;
l_state(l_state.count) := :new.state;
end before each row;
after statement is
declare
nb_stations_null number;
begin
for i in 1 .. l_station.count loop
SELECT COUNT(1)
INTO nbr_stations_null
FROM "TABLE_B"
WHERE "TABLE_B".STATE = l_state(i) AND
"TABLE_B".STATION <> l_station(i) AND
"TABLE_B".DATE_SENT IS NULL;
IF (nb_stations_null = 0) THEN
INSERT INTO "TABLE_A" VALUES (l_station(i), l_state(i));
END IF;
end after statement;
end;
/
Hello im trying to create a stock database for cars having 2 tables, carros and faturas, soo carros is the name of the car(veiculo) and how many cars i have in stock(quantidade) and how many cars i sold(vendidos), and the table faturas has the id and the car name(veiculo) the rest doesnt matter of my objective.
My objective is after i insert on table faturas the car name(veiculo) it goes to the carros table and it adds +1 to the vendidos of that car, but for now it is updating all my vendidos row.
Here it goes my code and i hope you can help me, Thanks.
CREATE TABLE carros (
veiculo VARCHAR(10) not null,
quantidade INTEGER not null,
vendidos INTEGER,
PRIMARY KEY (veiculo));
CREATE TABLE faturas (
id serial not null,
veiculo VARCHAR(10) NOT NULL,
matricula VARCHAR(10) NOT NULL,
nome CHAR(30) NOT NULL,
contacto VARCHAR(15) NOT NULL,
valor INT NOT NULL,
PRIMARY KEY (id),
FOREIGN KEY (veiculo) REFERENCES carros(veiculo));
CREATE OR REPLACE FUNCTION public.stockupdate()
RETURNS trigger
LANGUAGE plpgsql
AS $function$
begin
IF EXISTS(select c.veiculo from carros c, faturas f where c.veiculo = f.veiculo limit 1) THEN
UPDATE carros SET vendidos = vendidos + 1 from faturas where carros.veiculo = faturas.veiculo;
end if;
return new;
end;
$function$
;
CREATE OR REPLACE FUNCTION public.stockupdate()
RETURNS trigger
LANGUAGE plpgsql
AS $function$
begin
IF EXISTS(select c.veiculo from carros c, faturas f where c.veiculo = f.veiculo limit 1) THEN
UPDATE carros SET vendidos = vendidos + 1 from faturas where carros.veiculo = faturas.veiculo;
end if;
return new;
end;
$function$
;
create trigger stockupdatetrigger
before insert
on faturas
for each row
execute procedure stockupdate();
INSERT INTO carros
(veiculo, quantidade, vendidos)
VALUES('Opel', 10, 0);
INSERT INTO carros
(veiculo, quantidade, vendidos)
VALUES('Fiat', 10, 0);
INSERT INTO public.faturas
(veiculo, matricula, nome, contacto, valor)
VALUES('Opel', 'ABC 123', 'Ruben', '226-255243', 15000);
CREATE TABLE faturas (
id serial not null,
veiculo VARCHAR(10) NOT NULL,
matricula VARCHAR(10) NOT NULL,
nome CHAR(30) NOT NULL,
contacto VARCHAR(15) NOT NULL,
valor INT NOT NULL,
PRIMARY KEY (id),
FOREIGN KEY (veiculo) REFERENCES carros(veiculo)
);
CREATE OR REPLACE FUNCTION public.stockupdate()
RETURNS trigger
LANGUAGE plpgsql
AS $function$
begin
IF EXISTS(select c.veiculo from carros c, faturas f where c.veiculo = f.veiculo limit 1) THEN
UPDATE carros SET vendidos = vendidos + 1 from faturas where carros.veiculo = faturas.veiculo;
end if;
return new;
end;
$function$
;
create trigger stockupdatetrigger
before insert
on faturas
for each row
execute procedure stockupdate();
INSERT INTO carros
(veiculo, quantidade, vendidos)
VALUES('Opel', 10, 0);
INSERT INTO carros
(veiculo, quantidade, vendidos)
VALUES('Fiat', 10, 0);
INSERT INTO public.faturas
(veiculo, matricula, nome, contacto, valor)
VALUES('Opel', 'ABC 123', 'Ruben', '226-255243', 15000);
You seem to be overcomplicating things here. I think the trigger function you wanted to write is just:
create or replace function public.stockupdate()
returns trigger
language plpgsql
as $$
begin
update carros set vendidos = vendidos + 1 where veiculo = new.veiculo;
return new;
end;
$$;
Pseudo-table new can be used to access the row that is being inserted. You can use it to filter the carros table and update the correspoding record, if any
I have 3 tables:
create table customer
(
customer_id integer primary key,
customer_first_name varchar2(50) not null,
customer_surrname varchar2(50) not null,
phone_number varchar2(15) not null,
customer_details varchar2(200) default 'There is no special notes'
);
create table place
(
table_number integer primary key,
table_details varchar2(200) default 'There is no details'
);
create table booking
(
booking_id integer primary key,
date_of_booking date,
number_of_persons number(2) not null,
customer_id integer not null,
foreign key(customer_id) references customer(customer_id),
table_number integer not null,
foreign key(table_number) references place(table_number)
);
I have to generate customer table using this kind of generator:
set SERVEROUTPUT on format wrapped;
set define off;
drop sequence customer_seq;
drop sequence place_seq;
--CUSTOMER TABLE INSERT ROW GENERATOR
create sequence customer_seq START WITH 1 INCREMENT BY 1 NOMAXVALUE;
CREATE OR REPLACE TRIGGER customer_id_trigger
BEFORE INSERT ON customer
FOR EACH ROW
BEGIN
SELECT customer_seq.nextval INTO :new.customer_id FROM dual;
END;
/
DELETE FROM customer;
DECLARE
TYPE TABSTR IS TABLE OF VARCHAR2(250);
first_name TABSTR;
surrname TABSTR;
qname number(5);
phonenum number(15);
details TABSTR;
BEGIN
first_name := TABSTR ('Jhon','Poul','Jesica','Arnold','Max','Teemo','Tim','Mikel','Michael',
'Kristian','Adela','Mari','Anastasia','Robert','Jim','Juana','Adward',
'Jana','Ola','Kristine','Natali','Corey','Chester','Naomi','Chin-Chou');
surrname := TABSTR ('Grey','Brown','Robins','Chiho','Lee','Das','Edwins','Porter','Potter',
'Dali','Jordan','Jordison','Fox','Washington','Bal','Pitney','Komarowski',
'Banks','Albra','Shwiger');
details := TABSTR ('Exellent Customer','Good Customer','Always drunked','Left big tips',
'Bad Customer','Did not pay last bill','New Customer','VIP client');
qname := 100; — CHANGE THIS TO MANAGE HOW MANY ROWS YOU WANT TO BE ADDED
FOR i IN 1..qname LOOP
phonenum := dbms_random.value(111111111,999999999);
INSERT INTO customer VALUES (NULL, first_name(dbms_random.value(1,25)),
surrname(dbms_random.value(1,20)), phonenum, details(dbms_random.value(1,8)));
END LOOP;
DBMS_OUTPUT.put_line('Customers done!');
END;
/
--TABLE INSERT
DELETE FROM place;
create sequence place_seq start with 1 increment by 1;
insert into place values (place_seq.nextval, 'Near the window');
insert into place values (place_seq.nextval, default);
insert into place values (place_seq.nextval, 'Near the door');
insert into place values (place_seq.nextval, 'Near the window');
insert into place values (place_seq.nextval, 'Near the window');
insert into place values (place_seq.nextval, default);
insert into place values (place_seq.nextval, 'Near the door');
insert into place values (place_seq.nextval, 'Big table');
insert into place values (place_seq.nextval, default);
insert into place values (place_seq.nextval, 'Big table');
So the question is how can I insert client_id in "booking" table which have one of the numbers in "customers" table? Because every time I regenerate data in "Customers" table numbers are changing so I should somehow select numbers in an array and then randomly choose one of them from this array. The thing is I don't really know how to select from table to array. Can anybody help?
For PL/SQL version you can use BULK COLLECT and standard sys.odcinumberlist array.
create sequence booking_seq start with 1 increment by 1;
declare
customerIds sys.odcinumberlist;
placeIds sys.odcinumberlist;
number_of_generated_records number := 150; -- number of records to be generated
begin
-- fill the array of customer_id values
select customer_id
bulk collect into customerIds
from customer;
-- fill the array of place numbers
select table_number
bulk collect into placeIds
from place;
for i in 1..number_of_generated_records loop
insert into booking(booking_id,date_of_booking,number_of_persons,customer_id,table_number)
values(
booking_seq.nextval, -- booking_id
trunc(sysdate) + round(dbms_random.value(1,365)), -- date_of_booking
round(dbms_random.value(1,99)), -- number_of_persons
customerIds(round(dbms_random.value(1,customerIds.count))), -- customer_id
placeIds(round(dbms_random.value(1,placeIds.count))) -- table_number
);
end loop;
end;
But, for your case I would prefer pure sql:
insert into booking(booking_id,date_of_booking,number_of_persons,customer_id,table_number)
with
customer_subq as (
select customer_id, row_number() over (order by customer_id) rn from customer
),
place_subq as (
select table_number, row_number() over (order by table_number) rn from place
),
params as (
select 1500 number_of_generated_records,
(select count(1) from customer) customer_count,
(select count(1) from place) place_count
from dual
),
random_numbers as (
select round(dbms_random.value(1,1000)) random_number1,
round(dbms_random.value(1,1000)) random_number2,
round(dbms_random.value(1,1000)) random_number3,
round(dbms_random.value(1,1000)) random_number4
from dual,params
connect by level <= number_of_generated_records
)
select booking_seq.nextval booking_id,
trunc(sysdate) + mod(random_number1,365) date_of_booking,
mod(random_number1,100) number_of_persons,
customer_id,
table_number
from random_numbers,
params,
customer_subq,
place_subq
where mod(random_number1,customer_count) + 1 = customer_subq.rn
and mod(random_number2,place_count) + 1 = place_subq.rn
here's the value of ACCOUNT_NUMBER that has been generated by a sequence and inserted in ACCOUNTS table by ACCOUNT_NUMBER_TRIG trigger that i need to insert it into the TRANSACTION TABLE by the trigger ACCOUNTS_TRANSCATION_TRIG_1
CREATE OR REPLACE TRIGGER ACCOUNT_NUMBER_TRIG
BEFORE INSERT
ON ACCOUNTS
FOR EACH ROW
WHEN (NEW.ACCOUNT_NUMBER is not null)
DECLARE
V_ACC_NO ACCOUNTS.ACCOUNT_NUMBER%TYPE;
BEGIN
SELECT ACCOUNT_NO_SEQ.nextvaL INTO V_ACC_NO FROM DUAL;
:NEW.ACCOUNT_NUMBER := V_ACC_NO;
END ACCOUNT_NUMBER_TRIG;
------------------------------------------------------------------------------
CREATE OR REPLACE TRIGGER ACCOUNTS_TRANSCATION_TRIG_1 AFTER
INSERT ON ACCOUNTS FOR EACH ROW DECLARE CURSOR ACCOUNTS_CUR IS
SELECT ACCOUNT_NUMBER FROM ACCOUNTS;
DECLARE
TEMP_1 NUMBER(5,0);
BEGIN
SELECT ACCOUNTS.ACCOUNT_NUMBER FROM INSERTED INTO TEMP_1
OPEN ACCOUNTS_CUR;
INSERT
INTO TRANSACTIONS VALUES
(
SYSDATE,
- :NEW.ACCOUNT_NUMBER,
'NEW ACCOUNT',
0
);
CLOSE ACCOUNTS_CUR;
END ACCOUNTS_TRANSCATION_TRIG_1;
CREATE TABLE accounts(
ACCOUNT_NUMBER number,
ACCOUNT_NAME varchar2(20)
);
CREATE SEQUENCE ACCOUNT_NO_SEQ;
CREATE OR REPLACE TRIGGER ACCOUNT_NUMBER_TRIG
BEFORE INSERT
ON ACCOUNTS
FOR EACH ROW
WHEN (NEW.ACCOUNT_NUMBER is not null)
BEGIN
:NEW.ACCOUNT_NUMBER :=ACCOUNT_NO_SEQ.nextvaL;
END ACCOUNT_NUMBER_TRIG;
/
CREATE TABLE transactions(
TR_DATE date,
TR_ACCOUNT_NUMBER number,
TR_TYPE varchar2(20),
TR_somenumber int
);
CREATE OR REPLACE TRIGGER ACCOUNTS_TRANSCATION_TRIG_1 AFTER
INSERT ON ACCOUNTS FOR EACH ROW
BEGIN
INSERT INTO TRANSACTIONS( TR_DATE, TR_ACCOUNT_NUMBER, TR_TYPE, TR_somenumber )
VALUES
(
SYSDATE,
:NEW.ACCOUNT_NUMBER,
'NEW ACCOUNT',
0
);
END ACCOUNTS_TRANSCATION_TRIG_1;
/
INSERT INTO accounts( ACCOUNT_NUMBER, ACCOUNT_NAME ) VALUES (1111,'My Name' );
select * from accounts;
ACCOUNT_NUMBER ACCOUNT_NAME
-------------- --------------------
2 My Name
select * from transactions;
TR_DATE TR_ACCOUNT_NUMBER TR_TYPE TR_SOMENUMBER
---------- ----------------- -------------------- -------------
2017/07/11 2 NEW ACCOUNT 0
You can use CURVAL to get the most recent value returned by NEXTVAL:
CREATE OR REPLACE TRIGGER ACCOUNTS_TRANSCATION_TRIG_1 AFTER
INSERT ON ACCOUNTS FOR EACH ROW DECLARE CURSOR ACCOUNTS_CUR IS
BEGIN
INSERT
INTO TRANSACTIONS VALUES
(
SYSDATE,
- ACCOUNT_NO_SEQ.curval,
'NEW ACCOUNT',
0
);
CLOSE ACCOUNTS_CUR;
END ACCOUNTS_TRANSCATION_TRIG_1;
However in this case there is no need, as it has been used to set the ACOUNT_NUMBER:
INSERT
INTO TRANSACTIONS VALUES
(
SYSDATE,
- :NEW.ACCOUNT_NUMBER,
'NEW ACCOUNT',
0
);
BTW unless you are on an old version of Oracle this should work for first trigger:
CREATE OR REPLACE TRIGGER ACCOUNT_NUMBER_TRIG
BEFORE INSERT
ON ACCOUNTS
FOR EACH ROW
WHEN (NEW.ACCOUNT_NUMBER is not null)
BEGIN
:NEW.ACCOUNT_NUMBER := ACCOUNT_NO_SEQ.nextvaL;
END ACCOUNT_NUMBER_TRIG;
(I suspect the WHEN clause is wrong - should be when is null?)
I have contact details data in Oracle table something like below...
I want to insert new column to assign same ID to the contacts having matching information , i.e. based on group of lastname, firstname and (phone and/or email)
Output should look like below
I'm new to this forum, so having formatting issues while posting question, please see attached images for easy understanding of my requirement
Looking for PL/SQL procedure to get this done in our huge database
Due to the nature of the data you presented I see no possible way to do it in one SQL or for it to be better tuned.
Script to create the table and populate with your example:
create table TAB_TEST(LAST_NAME VARCHAR2(200),
FIRST_NAME VARCHAR2(200),
PHONE NUMBER,
EMAIL VARCHAR2(200))
/
INSERT INTO TAB_TEST VALUES
('Ho','Kim', 2058371579, 'ABC#GMAIL.COM')
/
INSERT INTO TAB_TEST VALUES
('Ho','Kim', 4479940000, 'ABC#GMAIL.COM')
/
INSERT INTO TAB_TEST VALUES
('Ho','Kim', 7195739945, 'XYZ#HOTMAIL.COM')
/
INSERT INTO TAB_TEST VALUES
('Ho','Kim', 7475393956, 'XYZ#HOTMAIL.COM')
/
INSERT INTO TAB_TEST VALUES
('Ho','Kim', 7475393956, 'XYZ#HOTMAIL.COM')
/
INSERT INTO TAB_TEST VALUES
('Ho','Kim', 7473430336, null)
/
INSERT INTO TAB_TEST VALUES
('Ho','Kim', 7195739945, 'XYZ#HOTMAIL.COM')
/
INSERT INTO TAB_TEST VALUES
('Ho','Kim', 7475393956, '123#HOTMAIL.COM')
/
INSERT INTO TAB_TEST VALUES
('Ho','Kim', null, '123#HOTMAIL.COM')
/
INSERT INTO TAB_TEST VALUES
('Ho','Kim', 1168548666, '456#HOTMAIL.COM')
/
INSERT INTO TAB_TEST VALUES
('Ho','Kim', 1168548666, '456#HOTMAIL.COM')
/
INSERT INTO TAB_TEST VALUES
('Ho','Kim', 1168548666, null)
/
INSERT INTO TAB_TEST VALUES
('Ho','Kim', 7473430336, null)
/
ALTER TABLE TAB_TEST ADD ID NUMBER
/
And the script to update
DECLARE
lncount NUMBER := 1;
-- had to create this record in order to add the rowid
TYPE lttab_test IS RECORD(
ROWID VARCHAR2(200),
last_name VARCHAR2(200),
first_name VARCHAR2(200),
phone NUMBER,
email VARCHAR2(200),
id NUMBER);
TYPE lttype
IS TABLE OF LTTAB_TEST;
larecords LTTYPE;
BEGIN
SELECT ROWID,
last_name,
first_name,
phone,
email,
id
bulk collect INTO larecords
FROM tab_test a;
FOR i IN 1..larecords.count LOOP
IF Larecords(i).id IS NULL THEN
FOR j IN 1..i-1 LOOP
IF Larecords(j).phone = Larecords(i).phone
AND Larecords(i).id IS NULL THEN
Larecords(i).id := Larecords(j).id;
exit;
END IF;
END LOOP;
FOR j IN 1..i-1 LOOP
IF Larecords(j).email = Larecords(i).email
AND Larecords(i).id IS NULL THEN
Larecords(i).id := Larecords(j).id;
exit;
END IF;
END LOOP;
IF Larecords(i).id IS NULL THEN
Larecords(i).id := lncount;
lncount := lncount + 1;
END IF;
END IF;
END LOOP;
forall i IN 1..larecords.count
UPDATE tab_test
SET id = Larecords(i).id
WHERE ROWID = Larecords(i).ROWID;
COMMIT:
END;
It isn't the most efficient / pretty code but it works in case you just want sequential numbers
DECLARE
I NUMBER := 0;
BEGIN
ALTER TABLE TABLE
ADD ID NUMBER;
COMMIT;
FOR REC IN (SELECT * FROM TABLE)
LOOP
IF(REC.ID IS NULL) THEN
UPDATE TABLE T SET ID = I
WHERE (T.EMAIL = REC.EMAIL OR T.PHONE = REC.PHONE)
AND ID IS NULL;
COMMIT;
I := I + 1;
END IF;
END LOOP;
END;