Error on delete trigger - sql-server

This trigger should delete a row from the parent table that is not deleted from the child table. The error is in the image below.
My code attempt:
CREATE TRIGGER ProductDeleted ON Product
for DELETE AS
BEGIN
DELETE FROM OrderItem
WHERE ProductID = (SELECT ProductID FROM DELETED)
END
help me please

You could simplify it by adding a CASCADE DELETE hint on a foreign key constraint such as
CREATE TABLE OrderItem
(
ID INT ,
ProductID NOT NULL UNIQUE
CONSTRAINT fk_Products
REFERENCES Products (ID) ON DELETE CASCADE
);
Since you already have a table, all you need to do is drop the constraint and recreate a new one.
ALTER TABLE OrderItem DROP
CONSTRAINT fk_ProductID;
ALTER TABLE OrderItem ADD
CONSTRAINT fk_ProductID
FOREIGN KEY (ID)
REFERENCES Product (ID)
ON DELETE CASCADE;
What this means, is that , any time you delete a record from the parent table (Product), child records from (OrderItem)will be deleted as well, so you dont have to use triggers, unless if you want to do some recording.
If you are really insisting on using triggers then you can tweak it a little bit like this :
ALTER TRIGGER ProductDeleted on Product
INSTEAD OF DELETE AS
BEGIN
SET NOCOUNT ON;
/* First we are deleting referenced columns in OrderItem table */
DELETE FROM OrderItem
where ProductID IN (select deleted.ID /* Columns from product Table */ from deleted)
/* Now we are doing actual delete statement */
DELETE FROM Products where ID IN (select deleted.ID from deleted)
END
But once again you should consider using ON CASCADE DELETE, its much simpler to setup, easier to maintain and you can have only one INSTEAD OF trigger per table, so if you ever need to do something more meaningful you would have to change this one, and add extra overhead.

Add SET NOCOUNT ON as the first thing in the trigger's body (after BEGIN).

Related

Using triggers for referential integrity

There are 2 tables in the database that contain the following columns:
department table with column dept_no (char(4), not null)
employee table with column dept_no (char(4), null)
The dept_no column needs to be defined as a primary key in the department table and a foreign key in the employee table using a trigger.
I thought this was the correct solution using the deleted and inserted virtual tables to update/delete the foreign key in the corresponding employee table:
CREATE TRIGGER trig_delete_dept_no
ON department
AFTER DELETE
AS
UPDATE employee
SET employee.dept_no = NULL
FROM deleted
WHERE employee.dept_no = deleted.dept_no
CREATE TRIGGER trig_update_dept_no
ON department
AFTER UPDATE
AS
UPDATE e
SET e.dept_no = i.dept_no
FROM employee e
INNER JOIN inserted i ON e.dept_no = i.dept_no
However, when I update the department dept_no row to a different value I do not see the corresponding dept_no update in the employee table:
UPDATE department
SET dept_no = 'd4'
WHERE dept_no = 'd3'
Deleting functions as expected. What am I doing wrong with the update trigger and how can I combine these two triggers into one trigger?
There is an issue in your design. The first thing is you should not use dept_no as PK (Primary Key). You need to have an IDENTITY or GUID column as Primary Key and refer to that column as FK (Foreign Key).
This way you won't need to worry about changing the dept_no.
The second point is you don't need trigger. you can use CASCADE option on DELETE action.
Find more information on CASCADE
Thank you FLICKER and SMor for helping me think through this. I do not believe the assignment wants us to modify the tables by adding IDENTITY or GUID columns and since we are to strictly use Triggers, this is the best solution I can come up with:
CREATE TRIGGER trig_delete_dept_no ON department AFTER DELETE AS
UPDATE employee
SET employee.dept_no = NULL
FROM deleted
WHERE employee.dept_no = deleted.dept_no
CREATE TRIGGER trig_update_dept_no ON department AFTER UPDATE AS
IF UPDATE(dept_no)
BEGIN
IF (SELECT employee.dept_no
FROM employee, inserted
WHERE employee.dept_no = inserted.dept_no) IS NULL
BEGIN
ROLLBACK TRANSACTION
RAISERROR ('Integrity constraint violation, TRIGGER:
trig_update_dept_no, TABLE: department',16,1)
END
ELSE PRINT 'Update successful'
END
This will allow updates to occur in the department as long as there are no orphaned records in the employee table.

Deleting a line associated with a record using stored procedure

I'm trying to create a stored procedure where I can delete all data pertaining to one name. This would include data in the Order table, Line_item table, and Customer table. Should I be sub-querying differently?
CREATE PROCEDURE DELETE_CUSTOMER -- Delete a customer
#cus_id_arg DECIMAL, -- customer's ID.
#first_name_arg VARCHAR(30), -- customer’s first name.
#last_name_arg VARCHAR(40), -- customer's last name.
#cust_balance_in_arg DECIMAL(12,2) -- customer's balance
AS -- This "AS" is required by the syntax of stored procedures.
BEGIN
-- Insert the new customer with a 0 balance.
DELETE FROM CUSTOMER
WHERE order_id IN (SELECT order_id FROM customer_order where customer_id = #cus_id_arg);
END;
CASCADE
Corresponding rows are deleted from the referencing table if that row is deleted from the parent table.
Add cascading constraints, and the delete will be easy
ALTER Customer_Order
ADD CONSTRAINT FK_CustomerID
FOREIGN KEY(Customer_ID) REFERENCES (Customer_ID)
ON DELETE CASCADE;
ALTER Line_Item
ADD CONSTRAINT FK_Order_ID
FOREIGN KEY(Order_ID) REFERENCES Customer_Order(Order_ID)
ON DELETE CASCADE;
Now, any row you delete it from Customer table will delete all corrrespending rows in Customer_Order table will be deleted, and the same for Line_Item table.
So you can write just DELETE FROM Customer WHERE Customer_ID = #Customer_ID;

Delete trigger not working when executed

Is there any posibility to delete from table2 (which has a reference from table1) after delete one row in table1? (eg: if I delete a CustomerID from Customers at the same time to delete the order he made (OrderID))
CREATE TRIGGER DelBilete
ON Curse
AFTER DELETE
AS
BEGIN
DELETE FROM Bilete
WHERE IDCursa IN (SELECT IDCursa FROM Curse)
END
There are two ways to do this: either with a cascading delete (which you set up with your foreign key constraint):
ALTER TABLE [dbo].[Orders] WITH NOCHECK ADD CONSTRAINT
[FK_Orders_Customer] FOREIGN KEY([customerId])
REFERENCES [dbo].[Customer] ([customerId])
ON DELETE CASCADE
... or with a trigger, as you have tried. (You might try something like this, actually. Disclaimer: this was from memory and not tested.)
CREATE TRIGGER DelBilete
ON Curse
AFTER DELETE
AS
BEGIN
DELETE FROM Bilete WHERE IDCursa IN(SELECT IDCursa FROM deleted)
END
GO
If you just delete where the foreign key is present in the primary key table, you'll delete all your orders (or whatever Bilete contains)!
ETA: On getting a foreign key constraint error:
The delete statement conflicted with the reference constraint
"FK_Bilete_Curse".
If you have a foreign key constraint, and you want to delete from the primary table (Curse) and have the related records deleted from the related table (Bilete), an AFTER trigger won't solve the problem, precisely because it is an AFTER trigger. It takes place after the records have been deleted. If they can't be deleted, the trigger doesn't fire. And if those records are referenced by other records, they can't be deleted.
So you then have two options:
The cascading delete, above;
An INSTEAD OF trigger. (See here for more details.)
An INSTEAD OF trigger in your case would look like this (again, from memory and documentation, not tested):
CREATE TRIGGER DelBilete
ON Curse
INSTEAD OF DELETE
AS
BEGIN
DELETE FROM Bilete WHERE IDCursa IN(SELECT IDCursa FROM deleted)
DELETE FROM Curse WHERE IDCursa IN (SELECT IDCursa from deleted)
END
GO
With an INSTEAD OF trigger, when you execute code to delete a record from Curse, the trigger runs instead. It deletes the related records from Bilete, and then executes the delete from Curse.
INSTEAD OF triggers do not run recursively, so the DELETE from Curse in the trigger doesn't force another run of the trigger.

Deleting tables with foreign key constraints

I have three tables
GroupTable
GroupId
Name
Each group has a one to many with Users
Users
UserId
Name
GroupId
And each User has one to many with 'Challenges'
Challenges
Name
UserId
I want to be able to delete a group with the users assigned to that specific group
I have tried this where I do manage to delete the Group based on id without getting a foreign key constraint error but ALL the users added to the user table and ALL the challenges get deleted as well
ALTER TABLE GroupTable NOCHECK CONSTRAINT ALL
ALTER TABLE UserTable NOCHECK CONSTRAINT ALL
ALTER TABLE Challanges NOCHECK CONSTRAINT ALL
DELETE FROM GroupTable
WHERE ID = #GroupId
DELETE FROM child
FROM Challanges as child
INNER JOIN UserTable AS parent
ON child.UserId = parent.ID
WHERE parent.GroupId = #GroupId
DELETE FROM parent
FROM UserTable AS parent
WHERE GroupId = GroupId
How can I ammend the above so that I only delete the gropu with the specific Users and there challenges assigned to the Group?
Do not disable the constraints for this, as it will compromise data integrity.
Either use on delete cascade option on the foreign keys, or delete the data from all three tables in the correct order, inside a single transaction.
To add on cascade delete to an existing foreign key you must use the alter table statement to drop the existing constraint and then add it again with the on delete cascade option:
ALTER TABLE table_name
DROP CONSTRAINT constraint_name
ALTER TABLE table_name
ADD CONSTRAINT constraint_name
FOREIGN KEY (column_name)
REFERENCES other_table_name(other_column_name) ON DELETE CASCADE
(of course, this can be done using ssms's design table window)
Deleting the rows from the related tables in the correct order will ensure you will encounter no problems with the existing foreign key constraints, wrapping all the delete statement in a single transaction will ensure you will only delete from every table or none at all:
DECLARE #GroupId int = 5
BEGIN TRY
BEGIN TRANSACTION
DELETE c
FROM Challanges c
INNER JOIN UserTable u ON(c.UserId = u.UserId)
WHERE u.GroupId = #GroupId
DELETE
FROM Users
WHERE GroupId = #GroupId
DELETE
FROM GroupTable
WHERE ID = #GroupId
COMMIT TRANSACTION
END TRY
BEGIN CATCH
IF ##TRANCOUNT > 0
ROLLBACK TRANSACTION
END CATCH

How do I use cascade delete with SQL Server?

I have 2 tables: T1 and T2, they are existing tables with data. We have a one to many relationship between T1 and T2. How do I alter the table definitions to perform cascading delete in SQL Server when a record from T1 is deleted, all associated records in T2 also deleted.
The foreign constraint is in place between them. I don't want to drop the tables or create a trigger to do the deletion for T2. For example, when I delete an employee, all the review record should be gone, too.
T1 - Employee,
Employee ID
Name
Status
T2 - Performance Reviews,
Employee ID - 2009 Review
Employee ID - 2010 Review
To add "Cascade delete" to an existing foreign key in SQL Server Management Studio:
First, select your Foreign Key, and open it's "DROP and Create To.." in a new Query window.
Then, just add ON DELETE CASCADE to the ADD CONSTRAINT command:
And hit the "Execute" button to run this query.
By the way, to get a list of your Foreign Keys, and see which ones have "Cascade delete" turned on, you can run this script:
SELECT
OBJECT_NAME(f.parent_object_id) AS 'Table name',
COL_NAME(fc.parent_object_id,fc.parent_column_id) AS 'Field name',
delete_referential_action_desc AS 'On Delete'
FROM sys.foreign_keys AS f,
sys.foreign_key_columns AS fc,
sys.tables t
WHERE f.OBJECT_ID = fc.constraint_object_id
AND t.OBJECT_ID = fc.referenced_object_id
ORDER BY 1
And if you ever find that you can't DROP a particular table due to a Foreign Key constraint, but you can't work out which FK is causing the problem, then you can run this command:
sp_help 'TableName'
The SQL in that article lists all FKs which reference a particular table.
Hope all this helps.
Apologies for the long finger. I was just trying to make a point.
You will need to,
Drop the existing foreign key constraint,
Add a new one with the ON DELETE CASCADE setting enabled.
Something like:
ALTER TABLE dbo.T2
DROP CONSTRAINT FK_T1_T2 -- or whatever it's called
ALTER TABLE dbo.T2
ADD CONSTRAINT FK_T1_T2_Cascade
FOREIGN KEY (EmployeeID) REFERENCES dbo.T1(EmployeeID) ON DELETE CASCADE
You can do this with SQL Server Management Studio.
→ Right click the table design and go to Relationships and choose the foreign key on the left-side pane and in the right-side pane, expand the menu "INSERT and UPDATE specification" and select "Cascade" as Delete Rule.
Use something like
ALTER TABLE T2
ADD CONSTRAINT fk_employee
FOREIGN KEY (employeeID)
REFERENCES T1 (employeeID)
ON DELETE CASCADE;
Fill in the correct column names and you should be set. As mark_s correctly stated, if you have already a foreign key constraint in place, you maybe need to delete the old one first and then create the new one.
ON DELETE CASCADE
It specifies that the child data is deleted when the parent data is deleted.
CREATE TABLE products
( product_id INT PRIMARY KEY,
product_name VARCHAR(50) NOT NULL,
category VARCHAR(25)
);
CREATE TABLE inventory
( inventory_id INT PRIMARY KEY,
product_id INT NOT NULL,
quantity INT,
min_level INT,
max_level INT,
CONSTRAINT fk_inv_product_id
FOREIGN KEY (product_id)
REFERENCES products (product_id)
ON DELETE CASCADE
);
For this foreign key, we have specified the ON DELETE CASCADE clause which tells SQL Server to delete the corresponding records in the child table when the data in the parent table is deleted. So in this example, if a product_id value is deleted from the products table, the corresponding records in the inventory table that use this product_id will also be deleted.
First To Enable ONCascade property:
1.Drop the existing foreign key constraint
2.add a new one with the ON DELETE CASCADE setting enabled
Ex:
IF EXISTS(SELECT 1 FROM sys.foreign_keys WHERE parent_object_id = OBJECT_ID(N'dbo.Response'))
BEGIN
ALTER TABLE [dbo].[Response] DROP CONSTRAINT [FK_Response_Request]
ALTER TABLE [dbo].[Response] WITH CHECK ADD CONSTRAINT [FK_Response_Request] FOREIGN KEY([RequestId])
REFERENCES [dbo].[Request] ([RequestId])
ON DELETE CASCADE
END
ELSE
BEGIN
ALTER TABLE [dbo].[Response] WITH CHECK ADD CONSTRAINT [FK_Response_Request] FOREIGN KEY([RequestId])
REFERENCES [dbo].[Request] ([RequestId])
ON DELETE CASCADE
END
Second To Disable ONCascade property:
1.Drop the existing foreign key constraint
2.Add a new one with the ON DELETE NO ACTION setting enabled
Ex:
IF EXISTS(SELECT 1 FROM sys.foreign_keys WHERE parent_object_id = OBJECT_ID(N'dbo.Response'))
BEGIN
ALTER TABLE [dbo].[Response] DROP CONSTRAINT [FK_Response_Request]
ALTER TABLE [dbo].[Response] WITH CHECK ADD CONSTRAINT [FK_Response_Request] FOREIGN KEY([RequestId])
REFERENCES [dbo].[Request] ([RequestId])
ON DELETE CASCADE
END
ELSE
BEGIN
ALTER TABLE [dbo].[Response] WITH CHECK ADD CONSTRAINT [FK_Response_Request] FOREIGN KEY([RequestId])
REFERENCES [dbo].[Request] ([RequestId])
ON DELETE NO ACTION
END
If the one to many relationship is from T1 to T2 then it doesn't represent a function and therefore cannot be used to deduce or infer an inverse function that guarantees the resulting T2 value doesn't omit tuples of T1 join T2 that are deductively valid, because there is no deductively valid inverse function. ( representing functions was the purpose of primary keys. ) The answer in SQL think is yes you can do it. The answer in relational think is no you can't do it. See points of ambiguity in Codd 1970. The relationship would have to be many-to-one from T1 to T2.
I think you cannot just delete the tables property what if this is actual production data, just delete the contents that dont affect the table schema.

Resources