How to delete all referential tables in Oracle? - sql

I have table test0. This table referenced by tables:
test1
test2
test3
....
test150
Does it possible write simple short query to delete 1 row in table test0 and if they exist - in all tables test1 ... test150 ?

Try this function to get delete script for all of the child rows of the given parent row:
Create or replace
FUNCTION FN_GET_DELETE_SCRIPT
( Parent_ID IN VARCHAR2, Parent_Table_Name in varchar2
) RETURN varchar2
AS
sql_statement varchar2(200);
script varchar2(4000);
n pls_integer;
Tot Pls_Integer := 0;
Cc_Id Varchar2(500):=Null;
Cursor allTables
Is
Select uc.table_name , ac.column_name
from user_constraints uc , ALL_CONS_COLUMNS AC
Where
R_Constraint_Name = (Select Constraint_Name From User_Constraints Where Constraint_Type = 'P' And Table_Name = upper(Parent_Table_Name))
And Ac.Owner = Uc.Owner
And AC.constraint_name = uc.constraint_name;
Begin
for t in allTables loop
execute immediate 'select count(*) from '||t.table_name ||' where '||t.column_name||' = :1' into n using Parent_ID;
If N > 0 Then
script:='Delete From ' ||t.table_name||' Where '||t.column_name||'='''||Parent_ID||''';'||chr(10)||script;
End if;
End loop;
Return Script;
END FN_GET_DELETE_SCRIPT;
Note that this function gives you a delete script for deleting immediate children of the given parent row.
So this function needs a bit of modification to find all descendants of the given parent rows!

Try like this
CREATE TABLE supplier
( supplier_id numeric(10) not null,
supplier_name varchar2(50) not null,
contact_name varchar2(50),
CONSTRAINT supplier_pk PRIMARY KEY (supplier_id)
);
CREATE TABLE products
(product_id numeric(10) not null,
supplier_id numeric(10) not null,
CONSTRAINT fk_supplier
FOREIGN KEY (supplier_id)
REFERENCES supplier(supplier_id)
ON DELETE CASCADE
);
According to your requirement first drop all constraints and recreate them using on delete cascade as shown in above axample
Delete the supplier, and it will delate all products for that supplier
but be careful while using on delete cascade "You can, by mistake, delete half of your database without even realizing it"

You can do:
alter table table_name drop constraint constraint_name; and then add new constraint with on delete cascade.

Related

How to remove check constraint?

I am having difficult to remove CHECK using Alter in sql. Can anyone help me please?
CREATE TABLE MyProject_COST (
ID int(4) NOT NULL UNIQUE,
detail varchar2(25) NOT NULL,
cost int(6) CONSTRAINT cost_project CHECK(cost>=500)
);
ALTER TABLE MyProject_COST ALTER COLUMN Cost int(6)
Oracle does have an alter table ... drop constraint syntax for this.
But since you created an anonymous constraint, so this is tricky - because you don't know the name of the constraint.
One option is to use dynamic SQL to retrieve the constraint name, and drop it with an execute immediate command:
declare
c_name varchar2(255 char);
begin
select c.constraint_name into c_name
from all_constraints c
join all_cons_columns cc
on cc.table_name = c.table_name
and cc.constraint_name = c.constraint_name
where
cc.table_name = 'MYPROJECT_COST'
and cc.column_name ='COST'
and c.constraint_type = 'C' ;
if c_name is not null then
execute immediate
'alter table myproject_cost drop constraint "' || c_name || '"';
end if;
end;
/
Demo on DB Fiddle:
create table myproject_cost (
id int not null unique,
detail varchar2(25) not null,
cost int check(cost >= 500)
);
insert into MyProject_COST(id, detail, cost) values(1, 'foo', 0);
-- ORA-02290: check constraint (FIDDLE_XUVVCZVSYWWROHKPBFUF.SYS_C0030623) violated
declare
c_name varchar2(255 char);
begin
select c.constraint_name into c_name
from all_constraints c
join all_cons_columns cc
on cc.table_name = c.table_name
and cc.constraint_name = c.constraint_name
where
cc.table_name = 'MYPROJECT_COST'
and cc.column_name ='COST'
and c.constraint_type = 'C' ;
if c_name is not null then
execute immediate
'alter table myproject_cost drop constraint "' || c_name || '"';
end if;
end;
/
-- 1 rows affected
insert into MyProject_COST(id, detail, cost) values(1, 'foo', 0);
-- 1 rows affected

Oracle Trigger Syntax Using Subquery

I'm trying to write a Trigger in Oracle Syntax which, upon entering a line into a particular table, checks that both values entered belong to some classification that is held in another table. My initial thought was to have a constraint on the table that included a subquery but Oracle doesn't seem to like that.
The select query I have written in the below works - but I'm not sure how to put it into a trigger - but essentially I need the trigger to ensure that EW1.OrgId and EW2.OrgId are the same. Any help is appreciated!
CREATE TABLE Organisation (
OrgId INTEGER PRIMARY KEY,
Name VARCHAR(40) NOT NULL
);
CREATE TABLE Person (
PersonId INTEGER PRIMARY KEY,
FirstName VARCHAR(45) NOT NULL,
LastName VARCHAR(45) NOT NULL
);
CREATE TABLE Employee (
PersonId INTEGER PRIMARY KEY REFERENCES PERSON (PersonId) ON DELETE CASCADE
);
CREATE TABLE Manager (
PersonId INTEGER PRIMARY KEY REFERENCES PERSON (PersonId) ON DELETE CASCADE
);
CREATE TABLE EnlistedWith (
OrgId INTEGER REFERENCES ORGANISATION (OrgId) ON DELETE CASCADE,
PersonId INTEGER REFERENCES PERSON (PersonId) ON DELETE CASCADE,
PRIMARY KEY(OrgId,PersonId)
);
CREATE TABLE SupervisedBy (
EmployeeId INTEGER REFERENCES Employee (PersonId) ON DELETE CASCADE,
ManagerId INTEGER REFERENCES Manager (PersonId) ON DELETE CASCADE,
CONSTRAINT PK_SupervisedBy PRIMARY KEY (EmployeeId, ManagerId)
);
CREATE TRIGGER SupervisorCompany
AFTER INSERT ON SupervisedBy
FOR EACH ROW
BEGIN
declare qty INTEGER := 0;
BEGIN
SELECT COUNT (*) into qty
FROM SupervisedBy SB
INNER JOIN EnlistedWith EW1 ON SB.ManagerId = EW1.PersonId
INNER JOIN EnlistedWith EW2 ON SB.EmployeeId = EW2.PersonId
and EW1.OrgId <> EW2.OrgId
IF qty <> 0
then Raise_Error (1234567, 'Manager and Employee are not Enlisted with same Organisation');
END IF;
END;
END;
You can refer to the columns of the owner of a Trigger using :NEW / :OLD. So, your Trigger could be re-written as
CREATE OR REPLACE TRIGGER supervisorcompany AFTER
INSERT
ON supervisedby FOR EACH ROW
DECLARE qty INTEGER := 0;
BEGIN
SELECT count (*)
INTO qty
FROM enlistedwith ew1
WHERE ew1.personid = :NEW.managerid
AND EXISTS
(
SELECT 1
FROM enlistedwith ew2
WHERE ew2.personid = :NEW.employeeid
AND ew1.orgid <> ew2.orgid ) ;
IF qty <> 0 THEN
raise_application_error (1234567, 'Manager and Employee are not Enlisted with same Organisation');
END IF;
END;
/
There's some guessing involved... Maybe something like this
CREATE TRIGGER SupervisorCompany
AFTER INSERT
ON SupervisedBy
FOR EACH ROW
BEGIN
IF (SELECT OrgId
FROM EnlistedWith
WHERE PersonId = New.ManagerId)
<>
(SELECT OrgId
FROM EnlistedWith
WHERE PersonId = New.EmployeeId) THEN
RAISE_ERROR(1234567, 'Manager and Employee are not Enlisted with same Organisation');
END IF;
END;
is what you want? It checks if the OrgId of the row where the PersonId equals the newly entered ManagerId is the same as the one for the newly entered EmployeeId. If not, your error is raised.
(Untested, as no DDL for the tables were provided.)

Stored procedure for deleting records in Oracle DBMS

Trying to figure out how to create a stored procedure that will delete a customer from the customer table, customer_order table and line_item table. Here are the tables.
CREATE TABLE customer(
customer_ID DECIMAL(10) NOT NULL,
customer_first VARCHAR(30),
customer_last VARCHAR(40),
customer_total DECIMAL(12, 2),
PRIMARY KEY (customer_ID));
CREATE TABLE customer_order (
order_id DECIMAL(10) NOT NULL,
customer_id DECIMAL(10) NOT NULL,
order_total DECIMAL(12,2),
order_date DATE,
PRIMARY KEY (ORDER_ID),
FOREIGN KEY (CUSTOMER_ID) REFERENCES customer);
CREATE TABLE line_item(
order_id DECIMAL(10) NOT NULL,
item_id DECIMAL(10) NOT NULL,
item_quantity DECIMAL(10) NOT NULL,
line_price DECIMAL(12,2),
PRIMARY KEY (ORDER_ID, ITEM_ID),
FOREIGN KEY (ORDER_ID) REFERENCES customer_order,
FOREIGN KEY (ITEM_ID) REFERENCES item);
Here what I have so far for the SP
CREATE OR REPLACE PROCEDURE DELETE_CUSTOMER(
customer_id_arg IN DECIMAL,
first_name_arg IN VARCHAR,
last_name_arg IN VARCHAR
) IS
BEGIN
DELETE FROM CUSTOMER
WHERE customer_id IN (SELECT customer_last
FROM Customer
WHERE customer_id = customer_id_arg);
END;
When I try to run it, it doesn't delete a record and I am confused as for what to do.
Here a sample code to delete data from a parent table cascading to all child tables using the database_dictionary, I had lots of fun writing it ;-)
Note the last line delete from person where pk = '38B567E2909447868ABDDF500B78F2A3'; can easily be generalised. Another thing to add, this script doesn't work if you have primary constraints of two columns or more...
declare
TYPE cur_typ IS REF CURSOR;
procedure delete_from_sub_table_first(p_current_table_name varchar2, l_parent_key_value varchar2) is
c cur_typ;
child_table_pm_key_value varchar2(255);
begin
for childConsRecord in (select ac.table_name child_table, acc.column_name Child_column, rac.table_name, racc.COLUMN_NAME, (select column_name from ALL_CONS_COLUMNS accpm, all_constraints acpm where accpm.constraint_name = acpm.constraint_name and acpm.CONSTRAINT_TYPE = 'P' and ac.TABLE_NAME = acpm.TABLE_NAME) child_table_pm_key
from ALL_CONS_COLUMNS acc, all_constraints ac, ALL_CONS_COLUMNS racc, all_constraints rac
where acc.CONSTRAINT_NAME = ac.CONSTRAINT_NAME
and ac.CONSTRAINT_TYPE = 'R'
and racc.CONSTRAINT_NAME = rac.CONSTRAINT_NAME
and rac.constraint_name = ac.R_CONSTRAINT_NAME
and rac.table_name = p_current_table_name) loop
OPEN c FOR 'select ' || childConsRecord.child_table_pm_key || ' child_value FROM ' || childConsRecord.child_table || ' where ' || childConsRecord.Child_column || ' = :1' using l_parent_key_value;
LOOP
FETCH c INTO child_table_pm_key_value;
EXIT WHEN c%NOTFOUND;
-- process row here
delete_from_sub_table_first(childConsRecord.child_table, child_table_pm_key_value);
END LOOP;
close c;
EXECUTE IMMEDIATE 'DELETE FROM ' || childConsRecord.child_table || ' where ' || childConsRecord.Child_column || ' = :1' using l_parent_key_value;
end loop;
end;
begin
delete_from_sub_table_first('PERSON', '38B567E2909447868ABDDF500B78F2A3');
delete from person where pk = '38B567E2909447868ABDDF500B78F2A3';
end;
/

On update triggering in oracle?

I create 2 tables employees and customers.The employees table code is here:
create table employees(
employeeNumber number not null,
lastName varchar2(30) not null,
firstName varchar2(30) not null,
email varchar2(50) not null,
officeCode varchar2(10) not null,
assignTo number default null,
jobTitle varchar2(100) not null,
primary key (employeeNumber),
foreign key (officeCode) references offices(officeCode),
foreign key (assignTo) references employees(employeeNumber)
);
here assignTo is a foreign key of his own table employees and employeeNumber is auto increment.Some insertion sample is here:
insert into employees (lastName,firstName,email,officeCode,jobTitle)
values ('hasan','rumy','md.rejaulhasanrumy#gmail.com','123','manager');
insert into employees (lastName,firstName,email,officeCode,assignTo,jobTitle)
values ('hasan','rakib','kalorakib#gmail.com','123', 1 ,'assistant manager');
The customer table code is here:
create table customers (
customerNumber number not null,
customerName varchar2(50) not null,
phone varchar2(20) not null,
address varchar2(70) not null,
city varchar2(50) not null,
postalCode varchar2(15) not null,
country varchar2(40) not null,
salesRepEmployeeNumber number default null,
primary key(customerNumber),
foreign key (salesRepEmployeeNumber) references employees (employeeNumber)
);
customerNumber is auto increment.some sample insertion is here:
insert into customers
(customerName,phone,address,city,postalCode,country,salesRepEmployeeNumber)
values ('roxy','017456','holy park','kolia','Z143','something',1);
Now I create a trigger which execute before update employeeNumber column of employees table for on update cascade and the code is here:
create or replace trigger employees_update
before update of employeeNumber on employees
for each row
begin
update employees
set
assignTo = :new.employeeNumber
where assignTo = :old.employeeNumber;
update customers set
salesRepEmployeeNumber = :new.employeeNumber
where salesRepEmployeeNumber = :old.employeeNumber;
end;
/
above all is right in oracle but the problem is when I update employees table.The update code is here:
update employees set employeeNumber = 134 where employeeNumber = 1;
the problem is here:
ORA-04091: table RUMY.EMPLOYEES is mutating, trigger/function may not see it
ORA-06512: at "RUMY.EMPLOYEES_UPDATE", line 2
ORA-04088: error during execution of trigger 'RUMY.EMPLOYEES_UPDATE'
1. update employees set employeeNumber = 134 where employeeNumber = 1;
As far I know it's a system problem so where I make mistake?Can not I make foreign key assignTo of employees table?Also notice that same thing work properly in mysql.Advance thanks for answering this long question.
As you have discovered, you cannot select from the same table that a row-level trigger is defined against; it causes a table mutating exception.
Then - assuming at least Oracle 11, this will need to be split into individual triggers in earlier versions
CREATE OR REPLACE TRIGGER employees_update
FOR UPDATE ON employees
COMPOUND TRIGGER
TYPE employeeNumberRec IS RECORD
(oldEmployeeNumber employees.employeeNumber%TYPE
,newEmployeeNumber employees.employeeNumber%TYPE);
TYPE employeeNumbersTbl IS TABLE OF employeeNumberRec;
g_employeeNumbers employeeNumbersTbl;
BEFORE STATEMENT
IS
BEGIN
-- Reset the internal employees table
g_employeeNumbers := employeeNumbersTbl();
END BEFORE STATEMENT;
AFTER EACH ROW
IS
BEGIN
-- Store the updated employees
IF :new.employeeNumber <> :old.employeeNumber THEN
g_employeeNumbers.EXTEND;
g_employeeNumbers(g_employeeNumbers.LAST).oldEmployeeNumber := :old.employeeNumber;
g_employeeNumbers(g_employeeNumbers.LAST).newEmployeeNumber := :new.employeeNumber;
END IF;
END AFTER EACH ROW;
AFTER STATEMENT
IS
BEGIN
-- Now update the child tables
FORALL l_index IN 1..g_employeeNumbers.COUNT
UPDATE employees
SET assignTo = g_employeeNumbers(l_index).newEmployeeNumber
WHERE assignTo = g_employeeNumbers(l_index).oldEmployeeNumber;
FORALL l_index IN 1..g_employeeNumbers.COUNT
UPDATE customers
SET salesRepEmployeeNumber = g_employeeNumbers(l_index).newEmployeeNumber
WHERE salesRepEmployeeNumber = g_employeeNumbers(l_index).oldEmployeeNumber;
END AFTER STATEMENT;
END;
EDIT
In addition you will need to make the foreign key constraints that reference this table deferred e.g.
CREATE TABLE employees
(employeeNumber NUMBER NOT NULL
,lastName VARCHAR2(30) NOT NULL
,firstName VARCHAR2(30) NOT NULL
,email VARCHAR2(50) NOT NULL
,officeCode VARCHAR2(10) NOT NULL
,assignTo NUMBER DEFAULT NULL
,jobTitle VARCHAR2(100) NOT NULL
,PRIMARY KEY (employeeNumber)
,FOREIGN KEY (officeCode)
REFERENCES offices (officeCode)
,FOREIGN KEY (assignTo)
REFERENCES employees (employeeNumber)
DEFERRABLE
INITIALLY DEFERRED
)
and
CREATE TABLE customers
(customerNumber NUMBER NOT NULL
,customerName VARCHAR2(50) NOT NULL
,phone VARCHAR2(20) NOT NULL
,address VARCHAR2(70) NOT NULL
,city VARCHAR2(50) NOT NULL
,postalCode VARCHAR2(15) NOT NULL
,country VARCHAR2(40) NOT NULL
,salesRepEmployeeNumber NUMBER DEFAULT NULL
,PRIMARY KEY (customerNumber)
,FOREIGN KEY (salesRepEmployeeNumber)
REFERENCES employees (employeeNumber)
DEFERRABLE
INITIALLY DEFERRED
)
Note: if the constraint is violated this will cause an error at COMMIT not after an individual DML statement.
You can re-write the above trigger in following ways to avoid the issues:
create or replace trigger employees_update
before update of employeeNumber on employees
for each row
begin
emp_upd_trg_proc(:new.employeeNumber,:old.employeeNumber);
update customers set
salesRepEmployeeNumber = :new.employeeNumber
where salesRepEmployeeNumber = :old.employeeNumber;
end;
/
create or replace procedure emp_upd_trg_proc
(new number, old number)
is
pragma autonomous_transaction;
begin
update employees
set
assignTo = new
where assignTo = old;
commit;
end;
/

What query creates a trigger to generate composite primary key with two fk?

I'm trying to write a command to create a trigger that generates the composite primary key. This pk is in turn based on two fk.
I'll write example tables to be more specific.
(Table I'm working on)
CREATE TABLE DB.MESSAGE (
TEXT CLOB NOT NULL,
SUBJECT VARCHAR2(2000) NOT NULL,
MSG_TYPE NUMBER(1) NOT NULL,
MAIL_ID NUMBER(10) NOT NULL
)
;
ALTER TABLE DB.MESSAGE ADD CONSTRAINT MSG_PK PRIMARY KEY ( MSG_TYPE, MAIL_ID ) ;
ALTER TABLE DB.MESSAGE ADD
(
CONSTRAINT MESSAGE_TYPE_ID_FK
FOREIGN KEY ( MSG_TYPE )
REFERENCES DB.TYPES ( TYPE_ID )
);
ALTER TABLE DB.MESSAGE ADD
(
CONSTRAINT MESSAGE_MAIL_FK
FOREIGN KEY ( MAIL_ID )
REFERENCES DB.EML_MAIL ( MAILTO_ID )
);
(Referenced tables)
CREATE TABLE DB.TYPES (
TYPE_ID NUMBER(13) NOT NULL,
NAME VARCHAR2(10) NOT NULL
)
;
CREATE TABLE DB.MAIL (
MAIL_ID NUMBER(10) NOT NULL,
MAIL VARCHAR2(350) NOT NULL
)
;
My query so far
create or replace
TRIGGER DB.TRG_MESSAGE_ID
BEFORE INSERT ON DB.MESSAGE
FOR EACH ROW
BEGIN
IF INSERTING THEN
IF :NEW."MSG_ID" IS NULL THEN
SELECT DB.TYPES.TYPE_ID ??????
INTO :NEW."MSG_ID" FROM dual;
END IF;
END IF;
END;
EDIT: So the thinking behind this question was that there would be a separated column with a concatenations of both keys that compose the composite key.
A friend told me this is wrong, you just put both fields as pk and that's that. Is this true?