sql delete cascade not working - sql

I have a Microsoft SQL Database with 2 tables: dog and cat.
"dog" table has a primary key column called "food", which is related to a column called "food" as well in the "cat" table, which acts as the foreign key.
The relationship between the tables has an "on delete cascade" rule set, so when I delete a row from "dog" table, the relveant rows from "cat" table should be deleted as well.
But the rows in "cat" table do net get deleted, they stay. I use the Microsoft SQL Database manager to delete the row in "dog" table.
Any idea why this happens? do I need to use a special delete sql command to delete a row in this manner?
//edit
the script for the tables is:
USE [VELES]
GO
/****** Object: Table [dbo].[Periods] Script Date: 01/18/2011 14:52:19 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[Periods](
[PeriodID] [int] IDENTITY(1,1) NOT FOR REPLICATION NOT NULL,
[PeriodName] [nvarchar](50) COLLATE Hebrew_CS_AS NULL,
[PeriodStartDate] [smalldatetime] NOT NULL,
[PeriodEndDate] [smalldatetime] NOT NULL,
CONSTRAINT [PK_Periods] PRIMARY KEY CLUSTERED
(
[PeriodID] ASC
)WITH (IGNORE_DUP_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
USE [VELES]
GO
/****** Object: Table [dbo].[Exams] Script Date: 01/18/2011 14:55:37 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
SET ANSI_PADDING ON
GO
CREATE TABLE [dbo].[Exams](
[ExamID] [int] IDENTITY(1,1) NOT NULL,
[ExamUserID] [char](7) COLLATE Hebrew_CS_AS NOT NULL,
[ExamBase] [tinyint] NOT NULL,
[ExamUserTimesAccessed] [tinyint] NULL,
[ExamMaxTimesToOpen] [tinyint] NOT NULL,
[ExamUserLastTimeOpened] [datetime] NULL,
[ExamUserLastTimeFinished] [datetime] NULL,
[ExamTimeToFinish] [int] NOT NULL,
[ExamPassGrade] [int] NOT NULL,
[ExamPeriod] [int] NOT NULL,
[ExamUserRank] [tinyint] NULL,
CONSTRAINT [PK_Exams] PRIMARY KEY CLUSTERED
(
[ExamID] ASC
)WITH (IGNORE_DUP_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
GO
SET ANSI_PADDING OFF
GO
USE [VELES]
GO
ALTER TABLE [dbo].[Exams] WITH CHECK ADD CONSTRAINT [FK_Exams_Bases] FOREIGN KEY([ExamBase])
REFERENCES [dbo].[Bases] ([BaseID])
ON UPDATE CASCADE
ON DELETE CASCADE
GO
ALTER TABLE [dbo].[Exams] WITH NOCHECK ADD CONSTRAINT [FK_Exams_Periods] FOREIGN KEY([ExamPeriod])
REFERENCES [dbo].[Periods] ([PeriodID])
ON UPDATE CASCADE
ON DELETE CASCADE
GO
ALTER TABLE [dbo].[Exams] WITH NOCHECK ADD CONSTRAINT [FK_Exams_Users] FOREIGN KEY([ExamUserID])
REFERENCES [dbo].[Users] ([UserID])
ON UPDATE CASCADE
ON DELETE CASCADE
NOT FOR REPLICATION
GO
ALTER TABLE [dbo].[Exams] CHECK CONSTRAINT [FK_Exams_Users]
GO
ALTER TABLE [dbo].[Exams] WITH CHECK ADD CONSTRAINT [UserRanks_Exams_FK1] FOREIGN KEY([ExamUserRank])
REFERENCES [dbo].[UserRanks] ([RankID])
ON UPDATE CASCADE
ON DELETE CASCADE

I've solved the problem.
In the relationship window, there was an option called Enforce Foreign Key Constraint, which was set to "No".
I set it to "Yes" and now row deletion works well.

Can you show your table structure more concretely?
It sound like you have the PK/FK the wrong way around.
Deleting the FK part (child) does not do anything to the PK record (parent).
Only when you delete the PK records does it cascade to the child records that link to it.

For people using SQL Server Management Studio:
I've absolutely seen cases where the UI has got out of sync with the DB, even if it you refreshed the keys list or opened a completely new instance.
For my case I have an Order which has DiscountedItem child items.
The way to check if things are out of sync is to right click on FK_DiscountedItem_Order and select Script Key as CREATE To Clipboard and then examine what you get :
You should get something like this :
ALTER TABLE [dbo].[DiscountedItem]
WITH NOCHECK
ADD CONSTRAINT [FK_DiscountedItem_Order] FOREIGN KEY([OrderId])
REFERENCES [dbo].[Order] ([OrderId])
ON DELETE CASCADE; --should be seeing this!
ALTER TABLE [dbo].[DiscountedItem]
CHECK CONSTRAINT [FK_DiscountedItem_Order];
Where you can clearly see DELETE CASCADE.
If you get something like the following, then the cascade rule isn't actually active despite what the UI may say :
ALTER TABLE [dbo].[DiscountedItem]
WITH CHECK
ADD CONSTRAINT [FK_DiscountedItem_Order] FOREIGN KEY([OrderId])
REFERENCES [dbo].[Order] ([OrderId]);
I just deleted it (had to actually delete it twice) and recreated it to get the correct SQL.
You may need to run something like this to check for 'orphaned' child rows :
select * from DiscountedItem
where DiscountedItem.orderid not in (select orderid from [order])
And then if it is safe to do so :
delete from DiscountedItem
where DiscountedItem.orderid not in (select orderid from [order])
Why did this happen?
I just added the constraint and immediately got a foreign key error because I had orphaned rows. Something then got confused and it thought cascade was enabled.
So before creating a new constraint in the UI I recommend you always check first for orphaned rows. You will have to delete them anyway if they exist.

Are you sure the column food in dog is the primary key of dog? If you have a table called food, then it's column food should be the primary key of food and a foreign key of dog (and cat as well). Then with on delete cascade deletions on food will cause the corresponding rows on dog and cat to be deleted.

If the cat table is the key for the foreign key, then deleting a row from dog will not delete a row from cat, rather it would work the other way around.

This seams to work just fine.
delete from Periods where PeriodID = 1
will delete one row from Periods and all rows from Exams that have ExamPeriod = 1

Related

Deleting rows without CASCADE DELETE?

I have a database in SQL Server, where I have one table for customers, and each customer can have multiple bookings, but a booking can belong only to one customer. The point is that I have written an API and then a client side app using WPF, but I just noticed that I cannot delete a customer without actually previously deleting the associated bookings with that customer. My T-SQL looks like this roughly:
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[Customer](
[Id] [int] IDENTITY(1,1) NOT NULL,
[FullName] [nvarchar](50) NOT NULL,
[DateOfBirth] [date] NOT NULL,
[Phone] [nvarchar](20) NOT NULL,
PRIMARY KEY CLUSTERED
(
[Id] ASC
))
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[Booking](
[Id] [int] IDENTITY(1,1) NOT NULL,
[Amount] [decimal](10,2) NOT NULL,
[CustomerId] [int] NOT NULL,
PRIMARY KEY CLUSTERED
(
[Id] ASC
))
ALTER TABLE [dbo].Booking WITH CHECK ADD CONSTRAINT [FK_Booking_Customer] FOREIGN KEY([CustomerId])
REFERENCES [dbo].[Customer] ([Id])
GO
ALTER TABLE [dbo].[Booking] CHECK CONSTRAINT [FK_Booking_Customer]
GO
Then, I have a delete stored procedure defined like this:
CREATE PROCEDURE DeleteCustomer
#Id int
AS
BEGIN
SET NOCOUNT ON;
DELETE FROM [dbo].[Customer]
WHERE Id = #Id
END
GO
But as I said I cannot delete a customer that has existing bookings. One way is surely to use CASCADE DELETE, but I don't want also the bookings to be deleted if the customer is deleted. Any idea how to overcome the problem or any workarounds?
The options I see are:
Make the foreign key column [CustomerId] nullable, and then use on delete set null
Use a soft delete on the Customer table, e.g. a bit column such as IsActive or IsDeleted.
Disable the foreign key: alter table [dbo].Booking nocheck constraint [FK_Booking_Customer]
Drop the foreign key.
In most situations I would implement the soft delete option.

How to insert a duplicate value in primary key column?

I have a table that has primary key on first column, but when I see the data available of this table, I find that records are repeated include primary key data. I want to know how can it be possible? Does primary column has repeated data in SQL Server 2008?
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
SET ANSI_PADDING ON
GO
CREATE TABLE [dbo].[DemoTbl](
[ProcedureId] [int] NOT NULL,
[ProcedureName] [nvarchar](100) NOT NULL,
[VersionNo] [char](5) NULL,
[PublishDate] [datetime] NULL,
[PublishUser] [varchar](50) NULL,
[SpecialtyId] [int] NOT NULL,
[ProcedureNumber] [varchar](20) NULL,
[PowerpointName] [nvarchar](100) NULL,
[Duration] [int] NOT NULL,
[LanguageId] [int] NOT NULL,
[TierId] [smallint] NOT NULL,
[PrintPdf] [bit] NULL,
[PresentationModeId] [tinyint] NULL,
CONSTRAINT [pk_DemoTbl] PRIMARY KEY CLUSTERED
(
[ProcedureId] ASC,
[LanguageId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
SET ANSI_PADDING OFF
GO
ALTER TABLE [dbo].[DemoTbl] WITH CHECK ADD FOREIGN KEY([PresentationModeId])
REFERENCES [dbo].[DemoTbl] ([PresentationModeId])
GO
ALTER TABLE [dbo].[DemoTbl] WITH CHECK ADD CONSTRAINT [FK_DemoTbl_Specialty] FOREIGN KEY([SpecialtyId])
REFERENCES [dbo].[Specialty] ([SpecialtyId])
GO
ALTER TABLE [dbo].[DemoTbl] CHECK CONSTRAINT [FK_DemoTbl_Specialty]
GO
ALTER TABLE [dbo].[DemoTbl] WITH CHECK ADD CONSTRAINT [FK_DemoTbl_TierMaster] FOREIGN KEY([TierId])
REFERENCES [dbo].[TierMaster] ([TierId])
GO
ALTER TABLE [dbo].[DemoTbl] CHECK CONSTRAINT [FK_DemoTbl_TierMaster]
GO
ALTER TABLE [dbo].[DemoTbl] ADD CONSTRAINT [DF_DemoTbl_CreationDate] DEFAULT (getdate()) FOR [CreationDate]
GO
ALTER TABLE [dbo].[DemoTbl] ADD CONSTRAINT [DF_DemoTbl_ModifiedDate] DEFAULT (getdate()) FOR [ModifiedDate]
GO
ALTER TABLE [dbo].[DemoTbl] ADD CONSTRAINT [DF_DemoTbl_IsActive] DEFAULT ((1)) FOR [IsActive]
GO
ALTER TABLE [dbo].[DemoTbl] ADD CONSTRAINT [DF_DemoTbl_LanguageId] DEFAULT ((1)) FOR [LanguageId]
GO
ALTER TABLE [dbo].[DemoTbl] ADD CONSTRAINT [DF_DemoTbl_ManageContent] DEFAULT ('false') FOR [ManageContent]
GO
ALTER TABLE [dbo].[DemoTbl] ADD CONSTRAINT [DF__Procedure__Print__1B14C01D] DEFAULT ((1)) FOR [PrintPdf]
GO
No, it is not possible in SQL Server to insert records that have duplicate values in the Primary Key.
It is not possible to have more than one row in the same table with the same primary key (PK).
If you are doing a SELECT and getting more than one row with the same PK could be because:
That table actually does not have a PK, maybe it was not properly created. You can check this by doing the following (it should return the PK column(s)):
SELECT * FROM INFORMATION_SCHEMA.COLUMNS WHERE table_name = 'TableName' AND COLUMN_KEY='PRI';
The SELECT statement is not correct. Maybe you are selecting from two or more tables and you are not relating them correctly.
To clarify, you can do the following:
1- Create a new table:
CREATE TABLE IF NOT EXISTS `Customer` (
`id` int(4),
`name` varchar(20),
`surname` varchar(40),
`phone` int(9),
PRIMARY KEY (`id`)
) ENGINE=INNODB;
Now you have a new table for storing customers identified by an ID.
2- Let's add some customers:
INSERT INTO Customer VALUES(111, 'John', 'White', 123456789),
(222, 'Bianca', 'Williams', 987654321),
(333, 'Mikel', 'Peterson', 421345642),
(444, 'Jolene', 'Gomez', 948113552);
3- If you try to insert a customer with an existing PK (id) you will get an error:
INSERT INTO Customer VALUES (222, 'Paul', 'Brown', 123412345);
4- In order to check the final table, you can do the following select:
SELECT * FROM Customer;
No it is not possible to have duplicate primary keys if the primary key constraint has been set to True.
Further to prevent duplicate primary keys set the Identity Specification to True
If this table is an intermediate table in a many to many connection the original columns are foreign keys and able to accept many instances of the same prodId(the whole point of the exercise). If someone then slams a PK on that column then no more duplicates can be added but if you query it the table will still return the original stuff in this guys screenshot.
This scenario can be seen in the Northwind sample database between Products and orders (OrderDetails table has composite PK on OrderID, ProdID but still shows duplicates of both). The PK was added afterwards, after data had been added.
The intermediate table is a link between the Many-to-Many tables and in general doesn't need a PK. Normalization 101.
For PK Constraint it create a Unique Index (Clustered / non Clustered ) on defined PK. If we disable indexes before loading that include PK Unique Index as well than we can insert duplicate PK in the column because the PK constraint is disabled. So while disabling Indexes please avoid PK index not be disable "is_primary_key=0 to disable all other indexes"
Tested -> it's possible if we create a primary key with nonclustered index. If we disable this index we will be able to insert duplicated record then. But - it won't be possible to enable (rebuild) the index back with duplicated values on primary key

Cannot create second cascading FK relationship

I have the following tables:
CREATE TABLE [dbo].[Memberships](
[MembershipId] [int] IDENTITY(1,1) NOT NULL,
[GroupId] [int] NOT NULL,
[UserId] [int] NOT NULL,
[IsUserAdmin] [bit] NOT NULL
)
CREATE TABLE [dbo].[Gifts](
[GiftId] [int] IDENTITY(1,1) NOT NULL,
[Name] [nvarchar](100) NOT NULL,
[MembershipId] [int] NOT NULL,
[ClaimedByMembershipId] [int] NULL
)
A membership can have many gifts associated with it. When a membership is deleted, I need to do two things:
Delete any gifts associated with that MembershipId.
For any records with ClaimedByMembershipId equal to the MembershipId of the Membership being deleted, set ClaimedByMembershipId to NULL.
I'm attempting to implement this cascading through Management Studio's UI. I can accomplish #1 by the following:
ALTER TABLE [dbo].[Gifts] WITH CHECK ADD CONSTRAINT [FK_Gifts_Memberships]
FOREIGN KEY([MembershipId])
REFERENCES [dbo].[Memberships] ([MembershipId])
ON DELETE CASCADE
GO
For #2, I'm unable to save the table after creating the key which I believe would look something like this:
ALTER TABLE [dbo].[Gifts] WITH CHECK ADD CONSTRAINT [FK_Gifts_Memberships1]
FOREIGN KEY([ClaimedByMembershipId])
REFERENCES [dbo].[Memberships] ([MembershipId])
ON NULL CASCADE
GO
I get the following error:
'Memberships' table saved successfully
'Gifts' table
- Unable to create relationship 'FK_Gifts_Memberships1'.
Introducing FOREIGN KEY constraint 'FK_Gifts_Memberships1' on table 'Gifts' may cause cycles
or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify
other FOREIGN KEY constraints.
Could not create constraint. See previous errors.
Thanks in advance.
Well it's exactly like the error says. You can't create cyclical cascade paths. If TableA can cause a cascading delete on TableB, then TableB cannot cause cascading deletes on TableA. Period. Even if in your specific case it logically seems like it should be ok, SQL Server won't allow it. No option to override.
Best you can do is handle your #2 requirement with a trigger.

Drop a table with primary key having foreign key reference to another table

Is there any way to drop a table having primary key and referenced by foreign key on another table? I know, If I will try to write a simple DROP statement then SSMS will throw me an exception saying
Msg 3726, Level 16, State 1, Line 1
Could not drop object 'dbo.Dept' because it is referenced by a FOREIGN KEY constraint.
May be the answer is simply NO but, I am looking for any work around as recently I was asked this in a Interview.
You have to drop the CONSTRAINT on the (child) table. That keeps the child table, but breaks the 'link' to the parent table.
Which is why I like to name my constraints. ("FK_EmployeeToDepartment" in this case).
IF EXISTS (SELECT * FROM dbo.sysobjects WHERE id = object_id(N'[dbo].[Employee]') and OBJECTPROPERTY(id, N'IsUserTable') = 1)
BEGIN DROP TABLE [dbo].[Employee]
END
IF EXISTS (SELECT * FROM dbo.sysobjects WHERE id = object_id(N'[dbo].[Department]') and OBJECTPROPERTY(id, N'IsUserTable') = 1)
BEGIN DROP TABLE [dbo].[Department]
END
CREATE TABLE [dbo].[Department](
[DepartmentUUID] [uniqueidentifier] NOT NULL,
[DepartmentName] [nvarchar](80) NULL,
[CreateDate] [datetime] NOT NULL
)
ALTER TABLE dbo.[Department] ADD CONSTRAINT PK_Department PRIMARY KEY NONCLUSTERED ([DepartmentUUID])
GO
ALTER TABLE [dbo].[Department] ADD CONSTRAINT CK_DepartmentName_Unique UNIQUE ([DepartmentName])
GO
CREATE TABLE [dbo].[Employee] (
[EmployeeUUID] [uniqueidentifier] NOT NULL,
[ParentDepartmentUUID] [uniqueidentifier] NOT NULL,
[SSN] [nvarchar](11) NOT NULL,
[LastName] [varchar](64) NOT NULL,
[FirstName] [varchar](64) NOT NULL,
[CreateDate] [datetime] NOT NULL,
[HireDate] [datetime] NOT NULL
)
GO
ALTER TABLE dbo.Employee ADD CONSTRAINT PK_Employee PRIMARY KEY NONCLUSTERED (EmployeeUUID)
GO
ALTER TABLE [dbo].[Employee] ADD CONSTRAINT CK_SSN_Unique UNIQUE (SSN)
GO
ALTER TABLE [dbo].[Employee] ADD CONSTRAINT FK_EmployeeToDepartment FOREIGN KEY (ParentDepartmentUUID) REFERENCES dbo.Department (DepartmentUUID)
GO
/* this will fail here */
--DROP TABLE [dbo].[Department]
GO
/* drop the constraint */
ALTER TABLE [dbo].[Employee] DROP CONSTRAINT FK_EmployeeToDepartment
GO
/* now it will work */
DROP TABLE [dbo].[Department]
GO
Drop the referenced table first and then parent table. That's why while creating reference you should choose ON DELETE CASCADE and/or ON UPDATE CASCADE.
It's purposefully throwing that error and stopping you from committing the mistake of making referenced table Orphan/Zombie.
If you really intend to drop a table that's referenced by a foreign key constraint, then the foreign key constraint is no longer meaningful, right? Drop the foreign key constraint first, then drop the table.
create table foo (
foo_id integer primary key
);
create table bar (
bar_id integer not null,
foo_id integer not null,
constraint bar_foo_id_fkey
foreign key (foo_id)
references foo (foo_id),
primary key (bar_id, foo_id)
);
drop table foo; -- Results in an error because of the foreign key constraint.
alter table bar
drop constraint bar_foo_id_fkey;
drop table foo; -- Drops table "foo".
You might still have work to do regarding existence of the column "bar"."foo_id", and regarding the primary key in "bar".

How can I stop the delete on a parent if a child entity that references that parent exists?

I have the following DDL that I am using with SQL Server 2012:
CREATE TABLE Subject (
[SubjectId] INT IDENTITY (1, 1) NOT NULL,
[Name] NVARCHAR (50) Not NULL,
CONSTRAINT [PK_Subject] PRIMARY KEY CLUSTERED ([SubjectId] ASC)
)
CREATE TABLE Topic (
[TopicId] INT IDENTITY (1, 1) NOT NULL,
[Name] NVARCHAR (50) NOT NULL,
[SubjectId] INT NOT NULL,
CONSTRAINT [PK_Topic] PRIMARY KEY CLUSTERED ([TopicId] ASC)
)
ALTER TABLE [Topic] WITH CHECK ADD CONSTRAINT [FK_TopicSubject]
FOREIGN KEY([SubjectId]) REFERENCES [Subject] ([SubjectId])
ON DELETE CASCADE
CREATE TABLE SubTopic (
[SubTopicId] INT IDENTITY (1, 1) NOT NULL,
[TopicId] INT NOT NULL,
[Name] NVARCHAR (4000) Not NULL,
CONSTRAINT [PK_SubTopic] PRIMARY KEY CLUSTERED ([SubTopicId] ASC)
)
ALTER TABLE [SubTopic] WITH CHECK ADD CONSTRAINT [FK_SubTopicTopic]
FOREIGN KEY([TopicId]) REFERENCES [Topic] ([TopicId])
ON DELETE CASCADE
When I try to run the scripts I get the following message:
{"Introducing FOREIGN KEY constraint 'FK_TopicSubject'
on table 'Topic' may cause cycles or multiple cascade paths.
Specify ON DELETE NO ACTION or ON UPDATE NO ACTION,
or modify other FOREIGN KEY constraints.\r\nCould not create constraint.
See previous errors."}
What I really need is for when a person tries to DELETE a subject when there are topics for the delete to fail. If I include neither DELETE ON CASCADE or DELETE NO ACTION then will this happen. If not then how can I stop the delete on subject happening if there are Topics for that subject?
Short answer is: If you don’t want cascade updates and deletions then use ON DELETE NO ACTION. Same applies for Update.
Here is a copy from MSDN article (it’s SQL Server 2000 but same rules still apply)
ON DELETE NO ACTION
Specifies that if an attempt is made to delete a row with a key referenced by foreign keys in existing rows in other tables, an error is raised and the DELETE is rolled back.
ON UPDATE NO ACTION
Specifies that if an attempt is made to update a key value in a row whose key is referenced by foreign keys in existing rows in other tables, an error is raised and the UPDATE is rolled back.
Please refer to this link. It has given a detail explanation of this error, and also has suggested to create a trigger as an alternative.
Foreign key constraint may cause cycles or multiple cascade paths?