J'ai une base de données SQL 2005 avec environ 250 tables.
Je souhaite activer temporairement ON DELETE CASCADE sur toutes les clés étrangères afin de pouvoir effectuer une suppression en bloc facilement.
Je veux ensuite désactiver ON DELETE CASCADE sur toutes les clés étrangères.
Le seul moyen que je connaisse consiste à utiliser Management Studio pour générer un script de création de base de données complète, effectuer une recherche et une substitution pour effacer tout le contenu sauf les clés étrangères, enregistrer le script, puis effectuer une recherche supplémentaire et remplacer pour ajouter le ON DELETE CASCADE.
Ensuite, j'exécute le script, supprime, puis l'autre script.
Existe-t-il un moyen plus simple de produire ce script? Cette méthode semble beaucoup trop sujette aux erreurs et je devrai tenir le script à jour avec les autres modifications apportées à la base de données ou le générer manuellement à chaque fois que je devrais l’utiliser.
Existe-t-il une option alternative pour exécuter une sélection sur les tables système afin de "générer" le script pour moi? Pourrait-il même être possible d'exécuter une mise à jour sur une table système qui active et désactive ON DELETE CASCADE?
Voici un script que j'ai utilisé dans un but similaire. Il ne prend pas en charge les clés étrangères composites (qui utilisent plusieurs champs.) Et il faudra probablement apporter quelques modifications avant que cela ne fonctionne dans votre cas. EDIT: En particulier, il ne gère pas correctement les clés étrangères multi-colonnes.
select
DropStmt = 'ALTER TABLE [' + ForeignKeys.ForeignTableSchema +
'].[' + ForeignKeys.ForeignTableName +
'] DROP CONSTRAINT [' + ForeignKeys.ForeignKeyName + ']; '
, CreateStmt = 'ALTER TABLE [' + ForeignKeys.ForeignTableSchema +
'].[' + ForeignKeys.ForeignTableName +
'] WITH CHECK ADD CONSTRAINT [' + ForeignKeys.ForeignKeyName +
'] FOREIGN KEY([' + ForeignKeys.ForeignTableColumn +
']) REFERENCES [' + schema_name(sys.objects.schema_id) + '].[' +
sys.objects.[name] + ']([' +
sys.columns.[name] + ']) ON DELETE CASCADE; '
from sys.objects
inner join sys.columns
on (sys.columns.[object_id] = sys.objects.[object_id])
inner join (
select sys.foreign_keys.[name] as ForeignKeyName
,schema_name(sys.objects.schema_id) as ForeignTableSchema
,sys.objects.[name] as ForeignTableName
,sys.columns.[name] as ForeignTableColumn
,sys.foreign_keys.referenced_object_id as referenced_object_id
,sys.foreign_key_columns.referenced_column_id as referenced_column_id
from sys.foreign_keys
inner join sys.foreign_key_columns
on (sys.foreign_key_columns.constraint_object_id
= sys.foreign_keys.[object_id])
inner join sys.objects
on (sys.objects.[object_id]
= sys.foreign_keys.parent_object_id)
inner join sys.columns
on (sys.columns.[object_id]
= sys.objects.[object_id])
and (sys.columns.column_id
= sys.foreign_key_columns.parent_column_id)
) ForeignKeys
on (ForeignKeys.referenced_object_id = sys.objects.[object_id])
and (ForeignKeys.referenced_column_id = sys.columns.column_id)
where (sys.objects.[type] = 'U')
and (sys.objects.[name] not in ('sysdiagrams'))
La réponse ci-dessus d'Andomar est bonne, mais ne fonctionne que pour les contraintes de clé étrangère à une colonne. Je l'ai un peu adapté aux contraintes multi-colonnes:
create function dbo.fk_columns (@constraint_object_id int)
returns varchar(255)
as begin
declare @r varchar(255)
select @r = coalesce(@r + ',', '') + c.name
from sys.foreign_key_columns fkc
join sys.columns c
on fkc.parent_object_id = c.object_id
and fkc.parent_column_id = c.column_id
where fkc.constraint_object_id = @constraint_object_id
return @r
end
select distinct
DropStmt = 'ALTER TABLE [' + ForeignKeys.ForeignTableSchema +
'].[' + ForeignKeys.ForeignTableName +
'] DROP CONSTRAINT [' + ForeignKeys.ForeignKeyName + '] '
, CreateStmt = 'ALTER TABLE [' + ForeignKeys.ForeignTableSchema +
'].[' + ForeignKeys.ForeignTableName +
'] WITH CHECK ADD CONSTRAINT [' + ForeignKeys.ForeignKeyName +
'] FOREIGN KEY(' + dbo.fk_columns(constraint_object_id) + ')' +
'REFERENCES [' + schema_name(sys.objects.schema_id) + '].[' +
sys.objects.[name] + '] '
+ ' ON DELETE CASCADE'
from sys.objects
inner join sys.columns
on (sys.columns.[object_id] = sys.objects.[object_id])
inner join (
select sys.foreign_keys.[name] as ForeignKeyName
,schema_name(sys.objects.schema_id) as ForeignTableSchema
,sys.objects.[name] as ForeignTableName
,sys.columns.[name] as ForeignTableColumn
,sys.foreign_keys.referenced_object_id as referenced_object_id
,sys.foreign_key_columns.referenced_column_id as referenced_column_id
,sys.foreign_keys.object_id as constraint_object_id
from sys.foreign_keys
inner join sys.foreign_key_columns
on (sys.foreign_key_columns.constraint_object_id
= sys.foreign_keys.[object_id])
inner join sys.objects
on (sys.objects.[object_id]
= sys.foreign_keys.parent_object_id)
inner join sys.columns
on (sys.columns.[object_id]
= sys.objects.[object_id])
and (sys.columns.column_id
= sys.foreign_key_columns.parent_column_id)
-- Uncomment this if you want to include only FKs that already
-- have a cascade constraint.
-- where (delete_referential_action_desc = 'CASCADE' or update_referential_action_desc = 'CASCADE')
) ForeignKeys
on (ForeignKeys.referenced_object_id = sys.objects.[object_id])
and (ForeignKeys.referenced_column_id = sys.columns.column_id)
where (sys.objects.[type] = 'U')
and (sys.objects.[name] not in ('sysdiagrams'))
Vous pouvez également utiliser la requête pour aider remove ON DELETE CASCADE
aux FK qui l’a actuellement.
Cela ne gère toujours pas le cas où les colonnes sont nommées de manière différente dans les deux tables - une autre fonction définie par l'utilisateur devra être définie pour cela.
Solution plus conforme aux normes:
;WITH CTE AS
(
SELECT
KCU1.CONSTRAINT_NAME AS FK_CONSTRAINT_NAME
,KCU1.TABLE_SCHEMA AS FK_SCHEMA_NAME
,KCU1.TABLE_NAME AS FK_TABLE_NAME
,KCU1.COLUMN_NAME AS FK_COLUMN_NAME
,KCU1.ORDINAL_POSITION AS FK_ORDINAL_POSITION
,KCU2.CONSTRAINT_NAME AS REFERENCED_CONSTRAINT_NAME
,KCU2.TABLE_SCHEMA AS REFERENCED_SCHEMA_NAME
,KCU2.TABLE_NAME AS REFERENCED_TABLE_NAME
,KCU2.COLUMN_NAME AS REFERENCED_COLUMN_NAME
,KCU2.ORDINAL_POSITION AS REFERENCED_ORDINAL_POSITION
FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS RC
INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE KCU1
ON KCU1.CONSTRAINT_CATALOG = RC.CONSTRAINT_CATALOG
AND KCU1.CONSTRAINT_SCHEMA = RC.CONSTRAINT_SCHEMA
AND KCU1.CONSTRAINT_NAME = RC.CONSTRAINT_NAME
INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE KCU2
ON KCU2.CONSTRAINT_CATALOG = RC.UNIQUE_CONSTRAINT_CATALOG
AND KCU2.CONSTRAINT_SCHEMA = RC.UNIQUE_CONSTRAINT_SCHEMA
AND KCU2.CONSTRAINT_NAME = RC.UNIQUE_CONSTRAINT_NAME
AND KCU2.ORDINAL_POSITION = KCU1.ORDINAL_POSITION
)
SELECT
FK_CONSTRAINT_NAME
--,FK_SCHEMA_NAME
--,FK_TABLE_NAME
--,FK_COLUMN_NAME
--,FK_ORDINAL_POSITION
--,REFERENCED_CONSTRAINT_NAME
--,REFERENCED_SCHEMA_NAME
--,REFERENCED_TABLE_NAME
--,REFERENCED_COLUMN_NAME
--,REFERENCED_ORDINAL_POSITION
,
'ALTER TABLE [' + FK_SCHEMA_NAME + ']'
+ '.[' + FK_TABLE_NAME + '] '
+ 'DROP CONSTRAINT [' + FK_CONSTRAINT_NAME + ']; '
AS DropStmt
,
'ALTER TABLE [' + FK_SCHEMA_NAME + ']'
+ '.[' + FK_TABLE_NAME + '] ' +
+ 'WITH CHECK ADD CONSTRAINT [' + FK_CONSTRAINT_NAME + '] '
+ 'FOREIGN KEY([' + FK_COLUMN_NAME + ']) '
+ 'REFERENCES [' + REFERENCED_SCHEMA_NAME + '].[' + REFERENCED_TABLE_NAME + ']([' + REFERENCED_COLUMN_NAME + ']) ON DELETE CASCADE; '
AS CreateStmt
FROM CTE
WHERE (1=1)
/*
AND FK_TABLE_NAME IN
(
'T_SYS_Geschossrechte'
,'T_SYS_Gebaeuderechte'
,'T_SYS_Standortrechte'
)
AND REFERENCED_TABLE_NAME NOT LIKE 'T_AP_Ref_Mandant'
*/
ORDER BY
FK_TABLE_NAME
,FK_CONSTRAINT_NAME
,FK_COLUMN_NAME
,FK_ORDINAL_POSITION
,REFERENCED_CONSTRAINT_NAME
,REFERENCED_TABLE_NAME
,REFERENCED_COLUMN_NAME
,REFERENCED_ORDINAL_POSITION
Modifier:
Étendu pour les clés étrangères multi-colonnes:
;WITH CTE AS
(
SELECT
KCU1.CONSTRAINT_NAME AS FK_CONSTRAINT_NAME
,KCU1.TABLE_SCHEMA AS FK_SCHEMA_NAME
,KCU1.TABLE_NAME AS FK_TABLE_NAME
,KCU1.COLUMN_NAME AS FK_COLUMN_NAME
,KCU1.ORDINAL_POSITION AS FK_ORDINAL_POSITION
,KCU2.CONSTRAINT_NAME AS REFERENCED_CONSTRAINT_NAME
,KCU2.TABLE_SCHEMA AS REFERENCED_SCHEMA_NAME
,KCU2.TABLE_NAME AS REFERENCED_TABLE_NAME
,KCU2.COLUMN_NAME AS REFERENCED_COLUMN_NAME
,KCU2.ORDINAL_POSITION AS REFERENCED_ORDINAL_POSITION
FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS RC
INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE KCU1
ON KCU1.CONSTRAINT_CATALOG = RC.CONSTRAINT_CATALOG
AND KCU1.CONSTRAINT_SCHEMA = RC.CONSTRAINT_SCHEMA
AND KCU1.CONSTRAINT_NAME = RC.CONSTRAINT_NAME
INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE KCU2
ON KCU2.CONSTRAINT_CATALOG = RC.UNIQUE_CONSTRAINT_CATALOG
AND KCU2.CONSTRAINT_SCHEMA = RC.UNIQUE_CONSTRAINT_SCHEMA
AND KCU2.CONSTRAINT_NAME = RC.UNIQUE_CONSTRAINT_NAME
AND KCU2.ORDINAL_POSITION = KCU1.ORDINAL_POSITION
)
SELECT
FK_SCHEMA_NAME
,FK_TABLE_NAME
,FK_CONSTRAINT_NAME
--,FK_COLUMN_NAME
--,REFERENCED_COLUMN_NAME
,
'ALTER TABLE ' + QUOTENAME(FK_SCHEMA_NAME) + '.' + QUOTENAME(FK_TABLE_NAME) + ' '
+ 'DROP CONSTRAINT ' + QUOTENAME(FK_CONSTRAINT_NAME) + '; '
AS DropStmt
,
'ALTER TABLE ' + QUOTENAME(FK_SCHEMA_NAME) + '.' + QUOTENAME(FK_TABLE_NAME) + '
ADD CONSTRAINT ' + QUOTENAME(FK_CONSTRAINT_NAME) + '
FOREIGN KEY('
+
SUBSTRING
(
(
SELECT ', ' + QUOTENAME(FK.FK_COLUMN_NAME) AS [text()]
FROM CTE AS FK
WHERE FK.FK_CONSTRAINT_NAME = CTE.FK_CONSTRAINT_NAME
AND FK.FK_SCHEMA_NAME = CTE.FK_SCHEMA_NAME
AND FK.FK_TABLE_NAME = CTE.FK_TABLE_NAME
FOR XML PATH, TYPE
).value('.[1]', 'nvarchar(MAX)')
,3, 4000
)
+ ')
'
+ ' REFERENCES ' + QUOTENAME(REFERENCED_SCHEMA_NAME) + '.' + QUOTENAME(REFERENCED_TABLE_NAME) + '('
+ SUBSTRING
(
(
SELECT ', ' + QUOTENAME(Referenced.REFERENCED_COLUMN_NAME) AS [text()]
FROM CTE AS Referenced
WHERE Referenced.FK_CONSTRAINT_NAME = CTE.FK_CONSTRAINT_NAME
AND Referenced.REFERENCED_SCHEMA_NAME = CTE.REFERENCED_SCHEMA_NAME
AND Referenced.REFERENCED_TABLE_NAME = CTE.REFERENCED_TABLE_NAME
FOR XML PATH, TYPE
).value('.[1]', 'nvarchar(MAX)')
, 3, 4000
)
+ ')
ON DELETE CASCADE
; ' AS CreateStmt
FROM CTE
GROUP BY
FK_SCHEMA_NAME
,FK_TABLE_NAME
,FK_CONSTRAINT_NAME
,REFERENCED_SCHEMA_NAME
,REFERENCED_TABLE_NAME
Et la version beaucoup plus simple pour PostGreSQL:
;WITH CTE AS
(
SELECT
KCU1.CONSTRAINT_NAME AS FK_CONSTRAINT_NAME
,KCU1.TABLE_SCHEMA AS FK_SCHEMA_NAME
,KCU1.TABLE_NAME AS FK_TABLE_NAME
,KCU1.COLUMN_NAME AS FK_COLUMN_NAME
,KCU1.ORDINAL_POSITION AS FK_ORDINAL_POSITION
,KCU2.CONSTRAINT_NAME AS REFERENCED_CONSTRAINT_NAME
,KCU2.TABLE_SCHEMA AS REFERENCED_SCHEMA_NAME
,KCU2.TABLE_NAME AS REFERENCED_TABLE_NAME
,KCU2.COLUMN_NAME AS REFERENCED_COLUMN_NAME
,KCU2.ORDINAL_POSITION AS REFERENCED_ORDINAL_POSITION
FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS RC
INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE KCU1
ON KCU1.CONSTRAINT_CATALOG = RC.CONSTRAINT_CATALOG
AND KCU1.CONSTRAINT_SCHEMA = RC.CONSTRAINT_SCHEMA
AND KCU1.CONSTRAINT_NAME = RC.CONSTRAINT_NAME
INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE KCU2
ON KCU2.CONSTRAINT_CATALOG = RC.UNIQUE_CONSTRAINT_CATALOG
AND KCU2.CONSTRAINT_SCHEMA = RC.UNIQUE_CONSTRAINT_SCHEMA
AND KCU2.CONSTRAINT_NAME = RC.UNIQUE_CONSTRAINT_NAME
AND KCU2.ORDINAL_POSITION = KCU1.ORDINAL_POSITION
)
SELECT
FK_SCHEMA_NAME
,FK_TABLE_NAME
,FK_CONSTRAINT_NAME
--,FK_COLUMN_NAME
--,REFERENCED_COLUMN_NAME
,
'ALTER TABLE ' || QUOTE_IDENT(FK_SCHEMA_NAME) || '.' || QUOTE_IDENT(FK_TABLE_NAME) || ' '
|| 'DROP CONSTRAINT ' || QUOTE_IDENT(FK_CONSTRAINT_NAME) || '; '
AS DropStmt
,
'ALTER TABLE ' || QUOTE_IDENT(FK_SCHEMA_NAME) || '.' || QUOTE_IDENT(FK_TABLE_NAME) || '
ADD CONSTRAINT ' || QUOTE_IDENT(FK_CONSTRAINT_NAME) || '
FOREIGN KEY(' || string_agg(FK_COLUMN_NAME, ', ') || ')
'
|| ' REFERENCES ' || QUOTE_IDENT(REFERENCED_SCHEMA_NAME) || '.' || QUOTE_IDENT(REFERENCED_TABLE_NAME) || '(' || string_agg(REFERENCED_COLUMN_NAME, ', ') || ')
ON DELETE CASCADE
; ' AS CreateStmt
FROM CTE
GROUP BY
FK_SCHEMA_NAME
,FK_TABLE_NAME
,FK_CONSTRAINT_NAME
,REFERENCED_SCHEMA_NAME
,REFERENCED_TABLE_NAME
Vous devrez modifier la table, supprimer les contraintes FK et les recréer:
C'est la syntaxe db2. SQLServer devrait être similaire
ALTER TABLE emp DROP CONSTRAINT fk_dept;
ALTER TABLE emp ADD CONSTRAINT fk_dept
FOREIGN KEY(dept_no)
REFERENCES dept(deptno)
ON DELETE CASCADE;
Vous pouvez écrire votre propre sp pour interroger la table système pour connaître toutes les clés étrangères, les supprimer et les recréer. Pour ce faire, vous devrez utiliser sql dynamique dans votre sp. Vous pourrez ainsi parcourir le fichier fk defn, les placer dans un fichier varchar et ajouter/modifier pour inclure CASCADE, puis exécuter stmt.
La réponse de @Andomar a fonctionné pour moi, mais c'était un peu manuel: vous devez l'exécuter, puis copier les résultats et les exécuter. J'avais besoin de l'utiliser dans le cadre de la configuration de mon test automatisé. J'avais donc besoin de l'exécuter automatiquement dans une requête.
Je suis venu avec ce qui suit qui obtient tout le SQL à exécuter afin de modifier les contraintes de clé étrangère, puis exécute tout en une fois:
IF Object_id('tempdb..#queriesForContraints') IS NOT NULL
BEGIN
DROP TABLE #queriesForContraints
END
DECLARE @ignoreTablesCommaSeparated VARCHAR(1000)
SELECT 'ALTER TABLE ['
+ ForeignKeys.foreigntableschema + '].['
+ ForeignKeys.foreigntablename
+ '] DROP CONSTRAINT ['
+ ForeignKeys.foreignkeyname + ']; '
+ 'ALTER TABLE ['
+ ForeignKeys.foreigntableschema + '].['
+ ForeignKeys.foreigntablename
+ '] WITH CHECK ADD CONSTRAINT ['
+ ForeignKeys.foreignkeyname
+ '] FOREIGN KEY(['
+ ForeignKeys.foreigntablecolumn
+ ']) REFERENCES ['
+ Schema_name(sys.objects.schema_id) + '].['
+ sys.objects.[name] + '](['
+ sys.columns.[name]
+ ']) ON DELETE CASCADE; ' AS query
INTO #queriesForContraints
FROM sys.objects
INNER JOIN sys.columns
ON ( sys.columns.[object_id] = sys.objects.[object_id] )
INNER JOIN (SELECT sys.foreign_keys.[name] AS
ForeignKeyName,
Schema_name(sys.objects.schema_id) AS
ForeignTableSchema,
sys.objects.[name] AS
ForeignTableName,
sys.columns.[name] AS
ForeignTableColumn,
sys.foreign_keys.referenced_object_id AS
referenced_object_id,
sys.foreign_key_columns.referenced_column_id AS
referenced_column_id
FROM sys.foreign_keys
INNER JOIN sys.foreign_key_columns
ON (
sys.foreign_key_columns.constraint_object_id =
sys.foreign_keys.[object_id] )
INNER JOIN sys.objects
ON ( sys.objects.[object_id] =
sys.foreign_keys.parent_object_id )
INNER JOIN sys.columns
ON ( sys.columns.[object_id] =
sys.objects.[object_id] )
AND ( sys.columns.column_id =
sys.foreign_key_columns.parent_column_id ))
ForeignKeys
ON ( ForeignKeys.referenced_object_id = sys.objects.[object_id] )
AND ( ForeignKeys.referenced_column_id = sys.columns.column_id )
WHERE ( sys.objects.[type] = 'U' )
AND ( sys.objects.[name] NOT IN (
'sysdiagrams' --add more comma separated table names here if required
) )
DECLARE @queryToRun NVARCHAR(MAX)
SELECT @queryToRun = STUFF(
(SELECT query + ''
FROM #queriesForContraints
FOR XML PATH (''))
, 1, 0, '')
EXEC sp_executesql @statement = @queryToRun
IF Object_id('tempdb..#queriesForContraints') IS NOT NULL
BEGIN
DROP TABLE #queriesForContraints
END
Une solution simple que j'ai trouvée consiste à exporter la base de données dans un fichier unique, à utiliser la fonction de recherche pour remplacer tous les NO ACTION to CASCADE, à supprimer la base de données et à importer le fichier modifié.
Les informations modifiées sur la base de données entre l'exportation et l'importation seront perdues.
créer une nouvelle procédure stockée, où le seul paramètre est le nom de la table à traiter. Dans cette procédure, vous devrez boucler sur sys.foreign_keys et sys.foreign_key_columns pour créer la syntaxe appropriée et créer la syntaxe, utilisez simplement un curseur et des impressions (KISS).
appelez cette procédure avec la syntaxe:
EXEC sp_msforeachtable 'YourProcedureName ''?'''
et il fonctionnera pour chaque table. Prenez et exécutez la sortie, et vous avez terminé.