SQL Server nested triggers - sql

I have three tables:
CREATE TABLE Rents
(
RentID INT IDENTITY NOT NULL PRIMARY KEY,
StartDate SMALLDATETIME,
EndDate SMALLDATETIME,
Price MONEY,
RealEstateID INT UNIQUE NOT NULL,
DealMadeByEmployeeID INT NOT NULL,
CONSTRAINT CHK_Rents CHECK (Price > 0 AND EndDate > StartDate),
CONSTRAINT FK_Rents_EstatesBasicInfo
FOREIGN KEY (RealEstateID) REFERENCES EstatesBasicInfo(RealEstateID),
CONSTRAINT FK_Rents_Employees
FOREIGN KEY (DealMadeByEmployeeID) REFERENCES Employees(EmployeeID)
);
CREATE TABLE Purchases
(
PurchaseID INT IDENTITY NOT NULL PRIMARY KEY,
DateBought SMALLDATETIME,
Price MONEY CHECK (Price>0),
RealEstateID INT UNIQUE NOT NULL,
DealMadeByEmployeeID INT NOT NULL,
CONSTRAINT FK_Purchases_EstatesBasicInfo
FOREIGN KEY (RealEstateID) REFERENCES EstatesBasicInfo(RealEstateID),
CONSTRAINT FK_Purchases_Employees
FOREIGN KEY (DealMadeByEmployeeID) REFERENCES Employees(EmployeeID)
);
CREATE TABLE EmployeesSalary
(
EmployeeID INT NOT NULL PRIMARY KEY,
CurrentSalary MONEY DEFAULT 0,-- на процент
MonthlySalesMade INT DEFAULT 0,
MonthlyRentsMade INT DEFAULT 0,
CONSTRAINT FK_EmployeesSalary_Employees
FOREIGN KEY (EmployeeID) REFERENCES Employees(EmployeeID),
CONSTRAINT CHK_EmployeesSalary
CHECK (CurrentSalary >= 0 AND MonthlySalesMade >= 0 AND MonthlyRentsMade >= 0)
);
Each of them has a trigger
CREATE TRIGGER tr_EmployeesSalaryPurchasesUpdate --при INSERT в Purchases таблицата
ON Purchases
AFTER INSERT
AS
BEGIN
UPDATE EmployeesSalary
SET EmployeesSalary.MonthlySalesMade = EmployeesSalary.MonthlySalesMade + 1
WHERE EmployeesSalary.EmployeeID IN (SELECT inserted.DealMadeByEmployeeID
FROM inserted
WHERE DateBought IS NOT NULL)
END
--Update на MonthlyRentsMade
GO
CREATE TRIGGER tr_EmployeesSalaryRentsUpdate --при INSERT в Rents таблицата
ON Rents
AFTER INSERT
AS
BEGIN
UPDATE EmployeesSalary
SET MonthlyRentsMade = MonthlyRentsMade + 1
WHERE EmployeesSalary.EmployeeID IN (SELECT inserted.DealMadeByEmployeeID
FROM inserted
WHERE StartDate IS NOT NULL)
END
The problem comes when I want to add a trigger to EmployeesSalary:
CREATE TRIGGER tr_EmployeesSalaryCurrentSalary
ON EmployeesSalary
AFTER INSERT
AS
BEGIN
UPDATE EmployeesSalary
SET CurrentSalary = CurrentSalary + ((MonthlySalesMade + MonthlyRentsMade) * 200)
WHERE EmployeeID IN (SELECT i.EmployeeID
FROM inserted AS i);
END
I want when I get an insert in the EmployeesSalary (by the other two triggers), the CurrentSalary to be updated (depending on the EmployeeID). The trigger cause any errors but it doesn't work. (I think this is called nested triggers not sure...) Where is my mistake?

I would prefer to use a computed column instead of a trigger for something like this. Here is an example. I also added ISNULL around the two columns in case you have a NULL. Otherwise the computation will always be NULL.
alter table EmployeesSalary
add ComputedSalary as CurrentSalary + ((isnull(MonthlySalesMade, 0) + isnull(MonthlyRentsMade, 0)) * 200) persisted

Since this is an after-insert trigger (recursion won't occur on update table). I think where you are making a mistake is using NULL values in calculations so I would try this:
CREATE TRIGGER tr_EmployeesSalaryCurrentSalary
ON EmployeesSalary
AFTER INSERT
AS
BEGIN
UPDATE es
SET CurrentSalary = coalesce(i.CurrentSalary, 0) + ((coalesce(i.MonthlySalesMade, 0) + coalesce(i.MonthlyRentsMade, 0)) * 200)
from EmployeesSalary as es
inner join inserted AS i
on i.EmployeeID = es.EmployeeID
END
Also, I don't like IN statements so I'm joining instead.

Related

Can I create a trigger with a set of instruction with DB2?

I'm working with DB2 and I have to make a trigger that after a certain update on 'Disponibilita' has to do two differentes operation with the table 'Promozioni'
here the schemas:
create table PROMOZIONI (
PID char(5) not null primary key,
Valore DEC(4,2) not null,
NumProdotti INT not null DEFAULT 0 );
create table DISPONIBILITA (
CodProdotto char(5) not null,
CodNegozio char(5) not null,
Quantita INT not null,
PID char(5) references PROMOZIONI,
primary key (CodProdotto, CodNegozio));
and this is the trigger that obviously doesn't work:
Create or replace trigger AggiornaNumProdotti
After Update on Disponibilita
referencing old as O new as N
for each row
update Promozioni p
SET NumProdotti=NumProdotti+1
Where N.PID is not null and N.PID=p.PID;
UPDATE Promozioni p2
SET NumProdotti=NumProdotti-1
WHERE O.PID is not null and O.PID=p2.PID;
is there any way to make a single trigger or i'm force to create two differentes ones for each specific instruction? Thanks a lot
For more than one query you need a BEGIN and END
create table PROMOZIONI (
PID char(5) not null primary key,
Valore DEC(4,2) not null,
NumProdotti INT not null DEFAULT 0 );
INSERT INTO PROMOZIONI VALUES ('1',1.2,0),
('2',1.2,0)
create table DISPONIBILITA (
CodProdotto char(5) not null,
CodNegozio char(5) not null,
Quantita INT not null,
PID char(5) references PROMOZIONI,
primary key (CodProdotto, CodNegozio));
INSERT INTO DISPONIBILITA VALUES ('1','1',1,'1')
Create or replace trigger AggiornaNumProdotti
After Update on Disponibilita
referencing old as O new as N
for each row
BEGIN
update Promozioni p
SET NumProdotti=NumProdotti+1
Where N.PID is not null and N.PID=p.PID;
UPDATE Promozioni p2
SET NumProdotti=NumProdotti-1
WHERE O.PID is not null and O.PID=p2.PID;
END;
UPDATE DISPONIBILITA SET PID = '2' WHERE PID = '1'
SELECT * FROM PROMOZIONI
PID
VALORE
NUMPRODOTTI
1
1.20
-1
2
1.20
1
fiddle

How to create trigger or procedure in Oracle express to automate values in a column of a table

How can I enter the value in the line_total column that would be made by the account (sales_line.line_total = product.unit_price * sales_line.line_qty)?
I would like to have trigger or a procedure to automate this. When I enter the values in the columns, it automatically triggers the trigger, doing that calculation and inserting the result in the column sales_line.line_total.
I would also like to automate the column sale.sale_total which would be the sum of the values column sales_line.line_total. Can you make a trigger or procedure for these two questions?
can you help me?
CREATE TABLE product (
product_id NUMBER(4) NOT NULL,
category_id NUMBER(4) NOT NULL,
p_desc VARCHAR2(40),
cpu VARCHAR2(14),
ram VARCHAR2(14),
capacity VARCHAR2(14),
screen_size VARCHAR2(14),
battery VARCHAR2(14),
unit_price NUMBER(7, 2),
colour VARCHAR2(14),
qty_stock NUMBER(4)
);
ALTER TABLE product ADD CONSTRAINT product_pk PRIMARY KEY ( product_id );
CREATE TABLE sale (
sale_id NUMBER(4) NOT NULL,
sale_date DATE,
customer_id NUMBER(4) NOT NULL,
employee_id NUMBER(4) NOT NULL,
sale_total NUMBER(7, 2)
);
ALTER TABLE sale ADD CONSTRAINT sale_pk PRIMARY KEY ( sale_id );
CREATE TABLE sales_line (
sale_id NUMBER(4) NOT NULL,
product_id NUMBER(4) NOT NULL,
line_qty NUMBER(4),
line_total NUMBER(7, 2)
);
ALTER TABLE sales_line ADD CONSTRAINT index_3 PRIMARY KEY ( sale_id,
product_id );
ALTER TABLE product
ADD CONSTRAINT product_p_category_fk FOREIGN KEY ( category_id )
REFERENCES p_category ( category_id );
ALTER TABLE sale
ADD CONSTRAINT sale_customer_fk FOREIGN KEY ( customer_id )
REFERENCES customer ( customer_id );
ALTER TABLE sale
ADD CONSTRAINT sale_employee_id_fk FOREIGN KEY ( employee_id )
REFERENCES employee ( employee_id );
ALTER TABLE sales_line
ADD CONSTRAINT sales_line_product_fk FOREIGN KEY ( product_id )
REFERENCES product ( product_id );
ALTER TABLE sales_line
ADD CONSTRAINT sales_line_sale_fk FOREIGN KEY ( sale_id )
REFERENCES sale ( sale_id );
Here are the triggers I have written so far:
CREATE OR REPLACE TRIGGER trg_line_total_ai AFTER INSERT OR UPDATE ON sales_line
FOR EACH ROW
DECLARE
lt_value NUMBER(7,2);
BEGIN
SELECT product.unit_price INTO lt_value FROM product;
UPDATE sales_line
SET line_total = :NEW.line_qty * lt_value
-- SET line_total = (line_qty * :NEW.unit_price)
WHERE product_id = :NEW.product_id;
--UPDATE sales_line
-- SET line_total = (line_qty * :OLD.unit_price)
--WHERE product.product_id = :OLD.product_id;
END;
CREATE OR REPLACE TRIGGER trg_sale_total_ai AFTER INSERT OR UPDATE ON sales_line
FOR EACH ROW
BEGIN
UPDATE sale
SET sale_total = (sale_total + :NEW.line_total)
WHERE sale_id = :NEW.sale_id;
UPDATE sale
SET sale_total = (sale_total + :OLD.line_total)
WHERE sale_id = :OLD.sale_id;
END;
You have major two issues. The first (as noted in other answers) is that the first trigger should be a before trigger that modifies the records in place. The logic for the trigger can also be simplified (as below)
The second issues is that you should be subtracting the old values in the second trigger. So:
CREATE OR REPLACE TRIGGER trg_line_total_ai
BEFORE INSERT OR UPDATE ON sales_line
FOR EACH ROW
BEGIN
SELECT :NEW.line_qty * p.unit_price
INTO :NEW.line_total
FROM product p
WHERE p.product_id = :NEW.product_id;
END;
CREATE OR REPLACE TRIGGER trg_sale_total_ai
AFTER INSERT OR UPDATE ON sales_line
FOR EACH ROW
BEGIN
UPDATE sale
SET sale_total = (sale_total + :NEW.line_total)
WHERE sale_id = :NEW.sale_id;
UPDATE sale
SET sale_total = (sale_total - :OLD.line_total)
WHERE sale_id = :OLD.sale_id;
END;
Note that the second trigger has two updates. This allows the sale_id to be updated. You can wrap this into a single update if you like:
UPDATE sale
SET sale_total = (sale_total +
(CASE WHEN sale_id = :NEW.sale_id THEN :NEW.line_total ELSE 0 END) -
(CASE WHEN sale_id = :OLD.sale_id THEN :OLD.line_total ELSE 0 END)
)
WHERE sale_id IN (:OLD.sale_id, :NEW.sale_id);
You need to be very careful with expressing this logic so it works for both updates and inserts. You should probably extend this trigger to work for deletes as well.
To update sale_line you must use a before trigger, then update is not necesary over this table. Only asign value to :New.line_total.
To update sale you can do it in the dame trigger.
Use following code instead. Please note the keyword "Before" instead of After in both triggers definitions
CREATE OR REPLACE TRIGGER trg_line_total_ai BEFORE INSERT OR UPDATE ON sales_line
FOR EACH ROW
DECLARE
lt_value NUMBER(7,2);
BEGIN
SELECT product.unit_price INTO lt_value FROM product;
UPDATE sales_line
SET line_total = :NEW.line_qty * lt_value
-- SET line_total = (line_qty * :NEW.unit_price)
WHERE product_id = :NEW.product_id;
--UPDATE sales_line
-- SET line_total = (line_qty * :OLD.unit_price)
--WHERE product.product_id = :OLD.product_id;
END;
CREATE OR REPLACE TRIGGER trg_sale_total_ai BEFORE INSERT OR UPDATE ON sales_line
FOR EACH ROW
BEGIN
UPDATE sale
SET sale_total = (sale_total + :NEW.line_total)
WHERE sale_id = :NEW.sale_id;
UPDATE sale
SET sale_total = (sale_total + :OLD.line_total)
WHERE sale_id = :OLD.sale_id;
END;

How to trigger a table to change the value of another table column

I've created three tables.
CREATE TABLE Clients
(
ClientID INT IDENTITY(1,1) PRIMARY KEY,
First_Name VARCHAR(50) NOT NULL,
Last_Name VARCHAR(50) NOT NULL,
)
CREATE TABLE Reservation
(
ReservationID INT IDENTITY(1,1) PRIMARY KEY,
ClientID INT FOREIGN KEY (ClientID) REFERENCES Clients(ClientID),
Reservation_paid VARCHAR(3) DEFAULT 'NO',
)
CREATE TABLE Payment
(
Payment_ID INT IDENTITY(1,1) PRIMARY KEY,
ClientID INT FOREIGN KEY (ClientID) REFERENCES Clients(ClientID),
ReservationID INT FOREIGN KEY (ReservationID) REFERENCES Reservation(ReservationID),
)
I would like to change the value of the column Reservation_paid to YES at the Reservation table whenever the Client does pay the reservation, and i want to do it automatically with trigger.
Example: If the ClientID at the Reservation table exists at the Payment table automatically the value of the Reservation_paid will set to YES.
Thank you in advance.
CREATE TRIGGER trgAfterInsert ON [dbo].[Payment]
FOR INSERT
AS
declare #ClientID int;
select #ClientID =i.ClientID from inserted i;
if update(ClientID)
UPDATE Reservation set Reservation_paid='Yes' WHERE
ClientID=#ClientID;
--PRINT 'AFTER INSERT trigger fired.'
After Insert Trigger should do something like this
UPDATE R
SET Reservation_paid = 'Yes'
FROM reservation R
WHERE EXISTS (SELECT 1
FROM INSERTED I
WHERE I.clientid = R.clientid
AND I.reservationid = R.reservationid)
CREATE TRIGGER trgAfterInsert ON [dbo].[Payment]
FOR INSERT
AS
declare #ClientID int;
select #ClientID =i.ClientID from inserted i;
insert into Reservation(ClientID,Reservation_paid)
values(#ClientID,'Yes');
--PRINT 'AFTER INSERT trigger fired.'
GO
Write a trigger that will work on table Reservation after any insert or update on ClientId column of table Payment. Then match the ClientID with ClientID column of Reservation table and update the corresponding Reservation_paid to YES.
Edit:
The trigger will be like this
CREATE TRIGGER `UpdateReservation_paid` AFTER INSERT OR UPDATE ON `Payment`
FOR EACH ROW BEGIN
AS
begin
update Reservation
SET Reservation_paid='YES'
Where NEW.ClientID = Reservation.ClientID
and NEW.ReservationID = Reservation.ReservationID
end

How to add values of primary key column into foreign key column of other table

How to add values of primary key column into foreign key column of other table: I'm using SQL Server 2012
CREATE TABLE CUSTOMERS( ID INT NOT NULL, NAME VARCHAR (20) NOT NULL, AGE INT NOT NULL,
ADDRESS CHAR (25) , SALARY DECIMAL (18, 2), PRIMARY KEY (ID));
CREATE TABLE ORDERS ( ID INT NOT NULL, DATE DATETIME, CUSTOMER_ID INT references
CUSTOMERS(ID), AMOUNT VARCHAR (255), PRIMARY KEY (ID));
Here i need to take all values from primary key table 'customers' from column 'ID' to foreign key table orders to column 'ID'
DECLARE #A INT, #DATE DATETIME, #C_ID INT, #AMOUNTS INT;
SET #A =1;
SET #DATE ='2009-10-08 00:00:00';
SET #C_ID = 100
SET #AMOUNTS=1000;
WHILE #A <= 7
BEGIN
SET #DATE = DATEADD(DAY,1,#DATE);
SET #C_ID = #C_ID + 1
SET #AMOUNTS = #AMOUNTS+100;
INSERT INTO ORDERS(ID, DATE, CUSTOMER_ID,AMOUNT)
SELECT ID, #DATE, #C_ID, #AMOUNTS FROM CUSTOMERS WHERE AGE like'%';
SET #A = #A+1;
END
You have set your Orders table up with a Foreign Key on Customer_ID that references Customers(ID). So you can you only add rows to Orders if the ID exists in Customers. Right now you are trying to insert 101-107 in that column every time, so check if those IDs exists in Customers.
It seems to me that you SHOULD want to insert the Customers.ID column in Orders.Customer_ID, rather than Orders.ID. Is that what you meant?

Adding a nullable foreign key

I have two tables built like this (this is just a simplified and non-proprietary example):
Person Table
-----------
p_Id, f_name, l_name
Job Table
----------
job_Id, job_desc
I want to add a foreign key column, Persons.job_Id, that can be nullable that references Job.job_Id (the PK) The reason is, the job may not be known in advance, so it could be null. Having an "Other" is not an option.
I had this so far but I'm getting "could not create constraint".
ALTER TABLE dbo.Person
ADD job_Id INT FOREIGN KEY (job_Id) REFERENCES dbo.Job(job_Id)
Try it in two steps:
ALTER TABLE dbo.Person ADD job_Id INT NULL;
ALTER TABLE dbo.Person ADD CONSTRAINT FL_JOB
FOREIGN KEY (job_Id) REFERENCES dbo.Job(job_Id);
Try it like this, WITH NOCHECK:
ALTER TABLE dbo.Person ADD job_Id INT NULL;
ALTER TABLE dbo.Person WITH NOCHECK ADD CONSTRAINT FL_JOB
FOREIGN KEY (job_Id) REFERENCES dbo.Job(job_Id);
Below is my solution with creating foreign key programmatically.
TestTable1 has substitute of FK that is either NULL or matches record in TestTable2.
TestTable2 has standard FK in TestTable1.
CREATE Table TestTable1 (ID1 int IDENTITY UNIQUE, ID2 int NULL);
GO
CREATE Table TestTable2 (ID2 int IDENTITY UNIQUE, ID1 int NOT NULL foreign key references TestTable1(ID1));
GO
CREATE procedure CreateTestRecord1 #ID2 int null AS
begin
if #iD2 IS NOT NULL AND NOT EXISTS(SELECT * from TestTable2 where ID2 = #ID2)
begin
RAISERROR('Cannot insert TestTable1 record. TestTable2 record with ID %d doesnt exist', 16, 1, #ID2);
return;
end
Insert into TestTable1(ID2) OUTPUT Inserted.ID1 Values(#ID2);
end
GO
CREATE procedure LinkTable1toTable2 #ID1 int, #ID2 int NULL as
begin
if #iD2 IS NOT NULL AND NOT EXISTS(SELECT * from TestTable2 where ID2 = #ID2)
begin
RAISERROR('Cannot update ID2 in TestTable1 record. TestTable2 record with ID %d doesnt exist', 16, 1, #ID2);
return;
end
update TestTable1 Set ID2=#ID2 where ID1=#ID1;
select ##ROWCOUNT;
endGO