Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/drupal/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Sql server 预览具有级联约束的记录的SQL删除_Sql Server - Fatal编程技术网

Sql server 预览具有级联约束的记录的SQL删除

Sql server 预览具有级联约束的记录的SQL删除,sql-server,Sql Server,我们有一个主“users”表和许多引用UserId的表 直接:UserId是子表中的FK或 间接:“孙子”表中的另一个FK引用子表中的记录,该记录又通过FK约束引用UserID 现在,如果一切都很完美,那么删除一个用户应该像删除主表中的用户一样简单,并且级联约束会使其通过其余的表。问题是,我们不能100%确定每个引用的表(直接或间接)中的每个FK关系是否都具有ON CASCADE约束。我们需要某种方法来发出删除命令,并观察SQL server实际要删除哪些表。我阅读并尝试了它,但它没有显示任

我们有一个主“users”表和许多引用UserId的表

  • 直接:UserId是子表中的FK或
  • 间接:“孙子”表中的另一个FK引用子表中的记录,该记录又通过FK约束引用UserID
现在,如果一切都很完美,那么删除一个用户应该像删除主表中的用户一样简单,并且级联约束会使其通过其余的表。问题是,我们不能100%确定每个引用的表(直接或间接)中的每个FK关系是否都具有ON CASCADE约束。我们需要某种方法来发出删除命令,并观察SQL server实际要删除哪些表。我阅读并尝试了它,但它没有显示任何级联到其中的表——只显示主表中的条目

以下是我尝试过的:

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

我认为您可以在触发器中执行此操作(分析[插入]表并搜索从属表)。 例如:您可以创建一个表,用于存储用于检测外键链接的查询:

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表中的层次结构。

若要检查整个数据库中引用UserMasterTable的引用约束,请使用信息模式视图

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,以及什么是“删除”和“更新”规则。从中,您可以快速看到哪些引用约束缺少所需的级联规则。不需要高兴地扣动扳机

要将其扩展到“孙子”引用,请再添加两个join子句。
要将其扩展到任意级别,请使用递归CTE。

推送时,我知道我可以从日志中拉出表影响,但这将是一个可怕、有趣的问题。可能我遗漏了一些内容,但如果缺少ON-CASCADE DELETE,这难道不会首先阻止删除成功吗?涉及多少个表,获取行计数,在事务中运行删除,然后再次获取计数,然后回滚是否可行?知道发生了什么变化的粒度可能只在表级别上就足够了。@a_horse_和_no_name:我澄清了dB如何知道on DELETE级联的问题constraint@a_horse_with_no_name:可能有些已在删除集NULL时定义为
,@Sid?谢谢!相当复杂的答案,我会尝试它的时候,我很快可以得到正确的设置