1

I have several tables with a foreign key constraint that has the option ON DELETE CASCADE. Every table belongs to the same schema called datasets.

I'm able to retrieve the complete list of tables using :

SELECT * 
FROM INFORMATION_SCHEMA.TABLES 
WHERE TABLE_SCHEMA ='datasets'

For each table I would like to remove the ON DELETE CASCADE option on the foreign key constraint named FK_[TABLENAME]_SerieID where [TABLENAME] corresponds to the name of the table (and SerieId is the same foreign key across tables).

I am able to perform the operation for a particular table, for instance the table called Table1 using :

ALTER TABLE  datasets.Table1
    DROP CONSTRAINT FK_Table1_SerieID

ALTER TABLE datasets.Table1
    ADD CONSTRAINT FK_Table1_SerieID
        FOREIGN KEY (Serie_Id) REFERENCES[dbo].[Serie](SerieID)
        ON DELETE NO ACTION
GO

I would like to perform the above operation for each table that belong to the schema datasets . I'm new to T-SQL and I don't know how to do it.

Should I use a cursor? Can you help me with this?

I'm using SQL Server 2016.

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Malick
  • 6,252
  • 2
  • 46
  • 59

1 Answers1

3

I would not reinvent the wheel. There is excellent script written by Aaron Bertrand: Drop and Re-Create All Foreign Key Constraints in SQL Server.

You could easily extend it to handle NO ACTION case and specific schema by adding simple WHERE restriction:

DECLARE @drop   NVARCHAR(MAX) = N'',
        @create NVARCHAR(MAX) = N'';

-- drop is easy,just build a simple concatenated list from sys.foreign_keys:
SELECT @drop += N'
ALTER TABLE ' + QUOTENAME(cs.name) + '.' + QUOTENAME(ct.name) 
    + ' DROP CONSTRAINT ' + QUOTENAME(fk.name) + ';'
FROM sys.foreign_keys AS fk
INNER JOIN sys.tables AS ct
  ON fk.parent_object_id = ct.[object_id]
INNER JOIN sys.schemas AS cs 
  ON ct.[schema_id] = cs.[schema_id]
WHERE delete_referential_action_desc <> 'NO_ACTION'  -- here 
    AND cs.name = 'datasets';

-- create is a little more complex. We need to generate the list of 
-- columns on both sides of the constraint, even though in most cases
-- there is only one column.
SELECT @create += N'
ALTER TABLE ' 
   + QUOTENAME(cs.name) + '.' + QUOTENAME(ct.name) 
   + ' ADD CONSTRAINT ' + QUOTENAME(fk.name) 
   + ' FOREIGN KEY (' + STUFF((SELECT ',' + QUOTENAME(c.name)
   -- get all the columns in the constraint table
    FROM sys.columns AS c 
    INNER JOIN sys.foreign_key_columns AS fkc 
    ON fkc.parent_column_id = c.column_id
    AND fkc.parent_object_id = c.[object_id]
    WHERE fkc.constraint_object_id = fk.[object_id]
    ORDER BY fkc.constraint_column_id 
    FOR XML PATH(N''), TYPE).value(N'.[1]', N'nvarchar(max)'),1,1,N'')
  + ') REFERENCES ' + QUOTENAME(rs.name) + '.' + QUOTENAME(rt.name)
  + '(' + STUFF((SELECT ',' + QUOTENAME(c.name)
   -- get all the referenced columns
    FROM sys.columns AS c 
    INNER JOIN sys.foreign_key_columns AS fkc 
    ON fkc.referenced_column_id = c.column_id
    AND fkc.referenced_object_id = c.[object_id]
    WHERE fkc.constraint_object_id = fk.[object_id]
    ORDER BY fkc.constraint_column_id 
    FOR XML PATH(N''), TYPE).value(N'.[1]', N'nvarchar(max)'),1,1,N'') + ');'
FROM sys.foreign_keys AS fk
INNER JOIN sys.tables AS rt -- referenced table
  ON fk.referenced_object_id = rt.[object_id]
INNER JOIN sys.schemas AS rs 
  ON rt.[schema_id] = rs.[schema_id]
INNER JOIN sys.tables AS ct -- constraint table
  ON fk.parent_object_id = ct.[object_id]
INNER JOIN sys.schemas AS cs 
  ON ct.[schema_id] = cs.[schema_id]
WHERE rt.is_ms_shipped = 0 AND ct.is_ms_shipped = 0
  AND delete_referential_action_desc <> 'NO_ACTION' -- here
  AND cs.name = 'datasets';
print(@drop);
print(@create);
-- ...

DBFiddle Demo


One warning! Please avoid adding ORDER BY.

This script uses

SELECT @drop += N'...'
<=>
SELECT @drop = @drop + N'...'

and it may start producing incorrect results. More nvarchar concatenation / index / nvarchar(max) inexplicable behavior

Lukasz Szozda
  • 162,964
  • 23
  • 234
  • 275