2012-11-23 112 views
8

我们有一个主“用户”表和许多表引用该用户ID或者预览SQL删除这对CASCADE约束

  • 直接记录:用户ID是在子表或
  • 一个FK
  • 间接地:'grandchild'表中的另一个FK引用了子表中的一条记录,这个FK又通过FK约束引用了UserID。

现在,如果一切都是完美的,删除用户应该是作为主表和ON CASCADE约束荡漾它通表的其余部分删除它们那么简单。问题是,我们不是100%确定每个引用(直接或间接)引用的每个表中的FK关系是否具有ON CASCADE约束。我们需要一些方法来发出该删除,并观察SQL服务器实际触及的表删除哪些表。我读this,并尝试过,但它不显示任何表级联成 - 只是在主表中的条目只有

这里是我的尝试:

DELETE umt 
OUTPUT DELETED.* 
FROM [OurAppDb].[dbo].[UserMasterTable] umt 
WHERE umt.UserId LIKE 'ABCDABCD-ABCD-ABCD-ABCD-ABCDABCDABCD' 

我怎样才能看到所有的表上面的查询会触摸?

注意:ON CASCADE约束是数据库中的一个约束,我们认为每建立一个表时,我们都会为表添加表。在一个表上

ALTER TABLE [dbo].[UserEmailPrefs] 
WITH CHECK ADD CONSTRAINT [FK_UserEmailPrefs_UserMasterTable_UserId] FOREIGN KEY([UserId]) 
REFERENCES [dbo].[UserMasterTable] ([UserId]) 
ON DELETE CASCADE 
GO 

ALTER TABLE [dbo].[UserEmailPrefs] CHECK CONSTRAINT [FK_UserEmailPrefs_UserMasterTable_UserId] 
GO 
+0

在推我知道我可以从日志中拉动表的影响,但这将是可怕的,有趣的问题/问题。 – Andrew

+1

也许我错过了一些东西,但如果缺少ON CASCADE DELETE,是不是会阻止DELETE成功呢? –

+0

涉及到多少个表?是否可以获得行计数,在事务中运行删除操作并再次获取计数,然后回滚?知道发生了什么变化的粒度只会在桌面级别,可能就足够了。 – Andrew

回答

0

添加它的例子,我觉得你可以在触发做到这一点(分析[插入]表和搜索次级表)。 作为例子:你可以创建表,该表将存储查询,用于检测外键链接:

IF NOT EXISTS (
     SELECT * 
     FROM [sys].tables T 
     WHERE T.NAME = 'FKCheck' 
     ) 
    CREATE TABLE FKCheck (
     TableName SYSNAME 
     ,ChildTable SYSNAME 
     ,QueryText NVARCHAR(MAX) 
     ) 
ELSE 
    EXEC('DROP TABLE FKCheck') 

然后从元数据中提取的动态查询填充

;WITH CTE_FKs 
AS (
    SELECT FK.NAME 
     ,OBJECT_name(fk.parent_object_id) AS ChildTable 
     ,OBJECT_name(fk.referenced_object_id) AS ParentTable 
     ,delete_referential_action_desc AS DeleteAction 
     ,MainTable.NAME AS MainTableColumn 
     ,ChildObject.NAME AS ChildColumnName 
    FROM sys.foreign_keys FK 
    INNER JOIN sys.foreign_key_columns FKC ON FKC.constraint_object_id = FK.object_id 
    INNER JOIN sys.columns AS ChildObject ON ChildObject.object_id = FKc.parent_object_id 
     AND FKC.parent_column_id = ChildObject.column_id 
    INNER JOIN sys.columns AS MainTable ON MainTable.object_id = FK.referenced_object_id 
     AND MainTable.column_id = FKC.referenced_column_id 
    ) 
    ,CTE_Tables 
AS (
    SELECT DISTINCT C.NAME 
     ,C.ParentTable 
     ,C.DeleteAction 
     ,C.ChildTable 
    FROM [CTE_FKs] C 
    ) 
INSERT INTO [dbo].[FKCheck] (
    TableName 
    ,ChildTable 
    ,QueryText 

    ) 
SELECT C.ParentTable,C.ChildTable 
    ,'IF EXISTS (select 1 from inserted INNER JOIN ' + QUOTENAME(C.ChildTable) + ' ON ' + STUFF((
      SELECT ' AND inserted.' + QUOTENAME(C2.MainTableColumn) + ' = ' + + QUOTENAME(C2.ChildTable) + '.' + QUOTENAME(C2.ChildColumnName) 
      FROM CTE_FKs C2 
      WHERE C2.ParentTable = C.ParentTable 
       AND C2.NAME = C.NAME 
      FOR XML PATH('') 
       ,TYPE 
      ).value('.', 'nvarchar(MAX)'), 1, 4, '') + ') 
RAISERROR(''Relation with ' + QUOTENAME(C.ChildTable) +':'+ CASE C.DeleteAction 
     WHEN 'CASCADE' 
      THEN ' data will be deleted' 
     WHEN 'SET_NULL' 
      THEN ' set as NULL' 
     WHEN 'NO_ACTION' 
      THEN ' no default action' 
     ELSE 'Unknown' 
     END + ''')' 
FROM [CTE_Tables] C 

你在表查询将看起来像:

IF EXISTS (select 1 from inserted INNER JOIN [UserEmailPrefs] ON inserted.[UserId] = [UserEmailPrefs].[UserId]) 
RAISERROR('Relation with [UserEmailPrefs]: no default action') 
IF EXISTS (select 1 from inserted INNER JOIN [UserEmail] ON inserted.[UserId] = [UserEmail].[UserId]) 
RAISERROR('Relation with [UserEmail]: set as NULL') 

然后在触发器可以执行查询打印消息:

DECLARE @TableName SYSNAME = 'UserMasterTable'; 
    DECLARE @sSQL NVARCHAR(MAX) = ''; 

    SELECT @sSQL += F.QueryText + CHAR(10) 
    FROM FKCheck F 
    WHERE F.TableName = @TableName; 
EXEC(@sSQL) 
ROLLBACK 

如果您需要分析更多“较远”的表格,则需要遍历FKCheck表格中的层次结构。

+0

谢谢!相当相关的答案,我会很快尝试一下,当我可以设置恰当的时候 – DeepSpace101

1

要检查整个数据库中引用UserMasterTable的引用约束,请使用INFORMATION_SCHEMA视图。

SELECT RC.CONSTRAINT_NAME, TU.TABLE_NAME, RC.DELETE_RULE, RC.UPDATE_RULE 
    FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS RC 
    INNER JOIN INFORMATION_SCHEMA.CONSTRAINT_TABLE_USAGE TU 
      ON RC.CONSTRAINT_CATALOG = TU.CONSTRAINT_CATALOG 
     AND RC.CONSTRAINT_NAME = TU.CONSTRAINT_NAME 
    INNER JOIN INFORMATION_SCHEMA.table_constraints TC 
      ON RC.unique_constraint_name = TC.CONSTRAINT_NAME 
    WHERE TC.TABLE_NAME='Users' 

这返回的参照约束目标UserMasterTable列表,并为每个人,这表引用UserMasterTable,什么ON DELETE和ON UPDATE规则。由此您可以快速查看哪些参考约束缺少所需的CASCADE规则。不需要触发快乐。

要将此扩展为“孙”引用,请再添加两个连接子句。 要将其扩展到任意级别,请进行递归CTE。