Deleting rows without checking foreign indexes, triggers, etc in SQL Server - sql-server

I have a table that is referenced by 11 other tables as foreign key constraint. If I try to delete a row from it, apparently it checks the other tables to see if it should cascade delete other records, which is making the delete take too long. I've tried disabling constraints in the table I'm deleting and the other tables but it's not working (on the results panel I still get 11 lines of "0 rows affected").
I've already deleted the children, what should I so it doesn't go on these tables to check for cascades? Here's how I've been testing this:
BEGIN TRANSACTION
ALTER TABLE Table1 NOCHECK CONSTRAINT ALL
ALTER TABLE Table2 NOCHECK CONSTRAINT ALL
DELETE FROM Table2
WHERE id IN (100, 101)
ALTER TABLE Table2 CHECK CONSTRAINT ALL
ALTER TABLE Table1 CHECK CONSTRAINT ALL
ROLLBACK

BTW: You are in pretty dangerous position of breaking data integrity. Try this code:
CREATE TABLE C1(ID INT IDENTITY(1,1) PRIMARY KEY);
GO
INSERT INTO C1 DEFAULT VALUES;
GO 10
GO
SELECT * INTO C2 FROM C1;
GO
ALTER TABLE C2 WITH CHECK ADD CONSTRAINT FK_C12 FOREIGN KEY(ID) REFERENCES C1 (ID);
GO
BEGIN TRANSACTION
SELECT is_not_trusted from sys.foreign_keys WHERE name = 'FK_C12';
ALTER TABLE C1 NOCHECK CONSTRAINT ALL
ALTER TABLE C2 NOCHECK CONSTRAINT ALL
DELETE FROM C1 WHERE ID = 5;
ALTER TABLE C2 CHECK CONSTRAINT ALL
ALTER TABLE C1 CHECK CONSTRAINT ALL
SELECT is_not_trusted from sys.foreign_keys WHERE name = 'FK_C12';
ROLLBACK
That means after your FK is not trusted anymore you might come up with different/slower execution plans and an additional and maybe unnecessary I/O

Related

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

Add primary key column in SQL table

I am student of RDBMS.
I have very basic question let say I have one existing Table in SQL server. What will be script to alter table.
Drop Column 'RowId' if exist.
Drop contraint if exist.
Add one new column 'RowId' into table.
Make this column as primary key.
Autoincrement type int.
In SQL Server 2005 or newer, you could use this script:
-- drop PK constraint if it exists
IF EXISTS (SELECT * FROM sys.key_constraints WHERE type = 'PK' AND parent_object_id = OBJECT_ID('dbo.YourTable') AND Name = 'PK_YourTable')
ALTER TABLE dbo.YourTable
DROP CONSTRAINT PK_YourTable
GO
-- drop column if it already exists
IF EXISTS (SELECT * FROM sys.columns WHERE Name = 'RowId' AND object_id = OBJECT_ID('dbo.YourTable'))
ALTER TABLE dbo.YourTable DROP COLUMN RowId
GO
-- add new "RowId" column, make it IDENTITY (= auto-incrementing)
ALTER TABLE dbo.YourTable
ADD RowId INT IDENTITY(1,1)
GO
-- add new primary key constraint on new column
ALTER TABLE dbo.YourTable
ADD CONSTRAINT PK_YourTable
PRIMARY KEY CLUSTERED (RowId)
GO
Of course, this script may still fail, if other tables are referencing this dbo.YourTable using foreign key constraints onto the pre-existing RowId column...
Update: and of course, anywhere I use dbo.YourTable or PK_YourTable, you have to replace those placeholder with the actual table / constraint names from your own database (you didn't mention what they were, in your question.....)
Note: this answer was added before questions update
Add new column (note: you can only have one IDENTITY column per table)
Drop old primary key
Add new primary key
Drop old column if needed
Sample script:
CREATE TABLE whatever (
OldPKColumn uniqueidentifier NOT NULL,
CONSTRAINT PK_whatever PRIMARY KEY (OldPKColumn)
)
ALTER TABLE whatever
ADD RowId int NOT NULL IDENTITY (1,1);
ALTER TABLE whatever
DROP CONSTRAINT PK_whatever;
ALTER TABLE whatever WITH CHECK
ADD CONSTRAINT PK_whatever PRIMARY KEY CLUSTERED (RowId);
ALTER TABLE whatever
DROP COLUMN oldPKcolumn;
And a random thought... are you trying to reset an IDENTITY column?
If so, then use DBCC CHECKIDENT
Just a comment to improve these great answers (can't use comments yet - I'm one reputation point away from that privilege) and as future reference for myself:
A new IDENTITY (autonumber) column can be added and made the primary key in a single statement as well:
ALTER TABLE [TableName] ADD [ColumnName] int IDENTITY PRIMARY KEY;
I prefer not to bother with constraint names when it doesn't help.
You can specify seed (and increment) values between parantheses after the IDENTITY keyword.

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.

Fixing broken foreign keys with ALTER TABLE ... WITH CHECK CHECK CONSTRAINT

I have a broken foreign key in SQL Server 2005. Here is a reproduction:
CREATE TABLE t2(i2 BIGINT NOT NULL PRIMARY KEY)
CREATE TABLE t1(i1 BIGINT NOT NULL PRIMARY KEY)
ALTER TABLE t1 ADD CONSTRAINT fk FOREIGN KEY (i1) REFERENCES t2 (i2)
ALTER TABLE t1 NOCHECK CONSTRAINT fk
INSERT INTO t1 (i1) VALUES (0)
If I subsequently run:
ALTER TABLE t1 WITH CHECK CHECK CONSTRAINT fk
I get the error:
The ALTER TABLE statement conflicted with the FOREIGN KEY constraint "fk". The conflict occurred in database "broken-fk", table "dbo.t2", column 'i2'.
I can fix this manually:
DELETE FROM t1 WHERE NOT EXISTS (SELECT 1 FROM t2 WHERE t2.i2 = t1.i1)
ALTER TABLE t1 WITH CHECK CHECK CONSTRAINT fk
but I'd prefer to do it in one step, as each presumably needs a table scan and the table is tens of gigabytes in size.
Is it possible to get the ALTER TABLE to fix the error (by dropping rows) rather than exiting with an error?
Thanks.
Is it possible to get the ALTER TABLE to fix the error
(by dropping rows) rather than exiting with an error?
The long and short - no.
Do it in two steps as you are familiar with.
You could stop at
ALTER TABLE t1 CHECK CONSTRAINT fk
which turns it on for new records but leaves the existing data alone?

SQL Server: How do I add a constraint to an existing table but only if the constraint does not already exist?

I need to add a constraint to an existing SQL server table but only if it does not already exist.
I am creating the constraint using the following SQL.
ALTER TABLE [Foo] ADD CONSTRAINT [FK_Foo_Bar] FOREIGN KEY ([BarId]) REFERENCES [Bar] ([BarId]) ON UPDATE CASCADE ON DELETE CASCADE
I'm hoping I can add some SQL to the begining of the SQL to test for the existence of the constraint but I have no idea how.
Personally I would drop the existing constraint, and recreate it - in case the one that is already there is in some way different
IF EXISTS (SELECT * FROM dbo.sysobjects WHERE id = object_id(N'[dbo].[MyFKName]') AND OBJECTPROPERTY(id, N'IsForeignKey') = 1)
ALTER TABLE dbo.MyTableName DROP CONSTRAINT MyFKName
GO
ALTER TABLE dbo.MyTableName ADD CONSTRAINT [MyFKName] ...
The current, more modern, code I am using is:
IF EXISTS (SELECT * FROM sys.foreign_keys WHERE object_id = OBJECT_ID(N'[dbo].[MyFKName]') AND parent_object_id = OBJECT_ID(N'[dbo].[MyTableName]'))
ALTER TABLE dbo.[MyTableName] DROP CONSTRAINT [MyFKName]
GO
ALTER TABLE dbo.[MyTableName] ADD CONSTRAINT [MyFKName] FOREIGN KEY ...
not sure if there is any advantage of checking sys.objects ... or sys.foreign_keys ... but at some point I decided on sys.foreign_keys
Starting with SQL2016 new "IF EXISTS" syntax was added which is a lot more readable:
-- For SQL2016 onwards:
ALTER TABLE dbo.[MyTableName] DROP CONSTRAINT IF EXISTS [MyFKName]
GO
ALTER TABLE dbo.[MyTableName] ADD CONSTRAINT [MyFKName] FOREIGN KEY ...
I'd recommend using the INFORMATION_SCHEMA.TABLE_CONSTRAINTS view. It's portable across different database engines:
SELECT COUNT(*)
FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS
WHERE TABLE_NAME='Foo'
AND CONSTRAINT_NAME='FK_Foo_Bar'
AND CONSTRAINT_TYPE='FOREIGN KEY'
Check if the constraint already exists before adding it -
IF NOT EXISTS(SELECT * FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS WHERE CONSTRAINT_NAME = 'FK_Foo_Bar')
BEGIN
ALTER TABLE dbo.MyTableName ADD CONSTRAINT [MyFKName] ...
END
Alter table tableName add constraint constraintname default 0 for columnname
You can provide constraintname as you want without single quote
Drop the default constraint and create your own. ALTER table TABLE_NAME drop constraint CONSTRAINT NAME
GO
ALTER TABLE [dbo].[TABLE_NAME] ADD CONSTRAINT [DF_TABLE_NAME_COLUMN_NAME] FOR [COLUMN_NAME]
Very simple:
IF OBJECT_ID('Schema.keyname') IS NULL
ALTER TABLE Schema.tablename ADD CONSTRAINT keyname...

Resources