tables studio management keys foreign establecer sql sql-server sql-server-2005

studio - sql server relationships between tables



Script SQL para alterar TODAS las claves foráneas para agregar ON DELETE CASCADE (7)

Aquí hay un script que utilicé para un propósito similar. No es compatible con claves externas compuestas (que usan más de un campo). Y probablemente necesite algunos ajustes antes de que funcione para su situación. EDITAR: en particular, no maneja correctamente las claves externas de varias columnas.

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''))

Tengo una base de datos SQL 2005 con aproximadamente 250 tablas.

Quiero habilitar temporalmente ON DELETE CASCADE a todas las Foreign Keys para que pueda hacer una eliminación masiva fácilmente.

Luego quiero desactivar ON DELETE CASCADE en todas las Foreign Keys.

La única manera que conozco de hacer esto es usar Management Studio para generar un script de creación de bases de datos completo, hacer algún tipo de búsqueda y reemplazo para quitar todo excepto Foreign Keys, guardar el script, luego hacer más búsquedas y reemplazar para agregar el ON DELETE CASCADE.

Luego ejecuto el script, hago mi eliminación y luego ejecuto el otro script.

¿Hay alguna manera más fácil de producir este script? Este método parece demasiado propenso a errores y tendré que mantener el script actualizado con cualquier otro cambio que realicemos en la base de datos, o volver a generarlo manualmente cada vez que necesite usarlo.

¿Es una opción alternativa ejecutar una selección en las tablas del sistema para "generar" el script para mí? ¿Podría incluso ser posible ejecutar una actualización en una tabla del sistema que habilita y deshabilita ON DELETE CASCADE?


La respuesta anterior de Andomar es buena, pero funciona solo para restricciones de clave externa de columna única. Lo adapté un poco para las restricciones de múltiples columnas:

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''))

También puede usar la consulta para ayudar a eliminar ON DELETE CASCADE de los FK que actualmente lo tienen.

Todavía no se maneja el caso en el que las columnas reciben nombres diferentes en las dos tablas, otra función definida por el usuario tendría que definirse para eso.


La respuesta de @Andomar funcionó para mí, pero fue un poco manual: tienes que ejecutarla, luego copiar los resultados y ejecutarlos. Necesitaba usar esto como parte de la configuración de prueba automática, así que necesitaba ejecutarla en una consulta automáticamente.

He encontrado lo siguiente que hace que todo el SQL se ejecute para modificar las restricciones de clave externa, y luego lo ejecuta todo de una vez:

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


Más solución conforme a los estándares:

;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

Editar:
Extendido para llaves foráneas de múltiples columnas:

;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

Y la versión mucho más simple para 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


Tendrás que modificar la tabla, eliminar las restricciones de FK y volver a crearlas:

Esta es la sintaxis db2. SQLServer debería ser similar

ALTER TABLE emp DROP CONSTRAINT fk_dept; ALTER TABLE emp ADD CONSTRAINT fk_dept FOREIGN KEY(dept_no) REFERENCES dept(deptno) ON DELETE CASCADE;

Puede escribir su propia sp para consultar en la tabla del sistema todas las claves foráneas, soltarlas y volver a crearlas. Tendrá que usar sql dinámico en su sp para hacer esto, donde puede recorrer el defn fk, ponerlos en varchar y anexar / editar para incluir CASCADE y luego ejecutar stmt.


Una solución simple que encontré es exportar la base de datos a un solo archivo, usar la función de búsqueda para reemplazar todas las NO ACTION a CASCADE, soltar la base de datos e importar el archivo editado.

La información cambiada en la base de datos entre la exportación y la importación se perderá.


crea un nuevo procedimiento almacenado, donde el único parámetro es el nombre de la tabla a procesar. En ese procedimiento, deberá pasar por encima de sys.foreign_keys y sys.foreign_key_columns para generar la caída correcta y crear la sintaxis, simplemente use un cursor y algunas copias (KISS).

llama a este procedimiento con la sintaxis:

EXEC sp_msforeachtable ''YourProcedureName ''''?''''''

y se ejecutará para cada mesa. Toma y ejecuta la salida, y listo.