如何在sql server中找到循环依赖表

Soo*_*raj 4 database stored-procedures common-table-expression sql-server-2008

目前我正在努力寻找数据库中表的依赖顺序.并且我坚持在数据库中的一些表的循环依赖的peoblem.

因为有些表是循环依赖的,所以我没有得到整个订单......

有没有办法在sql server中的任何数据库中找到循环依赖表,除了数据库图表?

Aar*_*and 9

你真的不需要购买工具来找到这些参考文献.

SELECT 
  OBJECT_SCHEMA_NAME(fk1.parent_object_id)
   + '.' + OBJECT_NAME(fk1.parent_object_id), 
  OBJECT_SCHEMA_NAME(fk2.parent_object_id)
   + '.' + OBJECT_NAME(fk2.parent_object_id)
FROM sys.foreign_keys AS fk1
INNER JOIN sys.foreign_keys AS fk2
ON fk1.parent_object_id = fk2.referenced_object_id
AND fk2.parent_object_id = fk1.referenced_object_id;
Run Code Online (Sandbox Code Playgroud)


Kar*_*ger 8

我偶然发现了我现在发现在几个地方复制的脚本。我认为它最初来自SQL Azure 团队博客2010 年的一篇关于:

在关系数据库的世界中,循环引用是模式结构,其中与表相关的外键创建一个循环。尝试同步两个强制执行外键的关系数据库时,循环引用会导致特殊类型的问题。由于这个问题,包含循环引用的数据库架构在同步和复制数据库时可以使用的工具中受到限制。本文将解释循环引用并演示用于确定您的数据库是否具有循环引用的 Transact-SQL 脚本。

它也在此处复制并归功于韦恩贝瑞。也许他在 Sql Azure 团队?

@Aaron_Bertrand 的回答非常简洁。为了完整起见,我认为值得添加这个脚本,因为它发现了更长的依赖链。该链接很难找到,我将在此处重现代码,而不仅仅是希望让下一个人更轻松的链接。

它并不简洁。

下面的 Transact-SQL 脚本使用递归游标来检测您的数据库架构中是否存在任何循环引用。它可以在您尝试将其与 SQL Azure 同步之前在您的 SQL Server 数据库上运行,也可以在您的 SQL Azure 数据库上运行。您可以在 SQL Server Management Studio 的查询窗口中运行它;输出将显示在消息部分。

如果您有循环引用,输出将如下所示:

dbo.City -> dbo.Author -> dbo.City dbo.Division -> dbo.Author -> dbo.City -> dbo.County -> dbo.Region -> dbo.Image -> dbo.Division dbo.State - > dbo.Image -> dbo.Area -> dbo.Author -> dbo.City -> dbo.County -> dbo.Region -> >dbo.State dbo.County -> dbo.Region -> dbo.Author -> dbo.City -> dbo.County dbo.Image -> dbo.Area -> dbo.Author -> dbo.City -> dbo.County -> dbo.Region -> dbo.Image dbo.Location -> dbo.Author - > dbo.City -> dbo.County -> dbo.Region -> dbo.Image -> dbo.Location dbo.LGroup -> dbo.LGroup dbo.Region -> dbo.Author -> dbo.City -> dbo.County -> dbo.Region dbo.Author -> dbo.City -> dbo.Author dbo.Area -> dbo.Author -> dbo.City -> dbo.County -> dbo.Region -> dbo.Image -> dbo。区域

每条线都是一个循环引用,带有创建该圆的表的链接列表。检测循环引用的 Transact-SQL 脚本如下...此代码适用于 SQL Azure 和 SQL Server。

SET NOCOUNT ON

-- WWB: Create a Temp Table Of All Relationship To Improve Overall Performance
CREATE TABLE #TableRelationships (FK_Schema nvarchar(max), FK_Table nvarchar(max),
    PK_Schema nvarchar(max), PK_Table nvarchar(max))

-- WWB: Create a List Of All Tables To Check
CREATE TABLE #TableList ([Schema] nvarchar(max), [Table] nvarchar(max))

-- WWB: Fill the Table List
INSERT INTO #TableList ([Table], [Schema])
SELECT TABLE_NAME, TABLE_SCHEMA
FROM INFORMATION_SCHEMA.TABLES 
WHERE Table_Type = 'BASE TABLE'

-- WWB: Fill the RelationShip Temp Table
INSERT INTO #TableRelationships(FK_Schema, FK_Table, PK_Schema, PK_Table)
SELECT
    FK.TABLE_SCHEMA,
    FK.TABLE_NAME,
    PK.TABLE_SCHEMA,
    PK.TABLE_NAME
FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS C
      INNER JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS FK ON 
        C.CONSTRAINT_NAME = FK.CONSTRAINT_NAME
      INNER JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS PK ON 
        C.UNIQUE_CONSTRAINT_NAME = PK.CONSTRAINT_NAME
      INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE CU ON 
        C.CONSTRAINT_NAME = CU.CONSTRAINT_NAME
      INNER JOIN (
            SELECT i1.TABLE_NAME, i2.COLUMN_NAME
            FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS i1
            INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE i2 ON
             i1.CONSTRAINT_NAME = i2.CONSTRAINT_NAME
            WHERE i1.CONSTRAINT_TYPE = 'PRIMARY KEY'
) PT ON PT.TABLE_NAME = PK.TABLE_NAME

CREATE TABLE #Stack([Schema] nvarchar(max), [Table] nvarchar(max))

GO

-- WWB: Drop SqlAzureRecursiveFind
IF  EXISTS (SELECT * FROM sys.objects WHERE object_id = 
    OBJECT_ID(N'[dbo].[SqlAzureRecursiveFind]') AND type in (N'P', N'PC'))
DROP PROCEDURE [dbo].[SqlAzureRecursiveFind]

GO

-- WWB: Create a Stored Procedure that Recursively Calls Itself
CREATE PROC SqlAzureRecursiveFind
    @BaseSchmea nvarchar(max),
    @BaseTable nvarchar(max),
    @Schmea nvarchar(max),
    @Table nvarchar(max),
    @Fail nvarchar(max) OUTPUT
AS

    SET NOCOUNT ON

    -- WWB: Keep Track Of the Schema and Tables We Have Checked
    -- Prevents Looping          
    INSERT INTO #Stack([Schema],[Table]) VALUES (@Schmea, @Table)

    DECLARE @RelatedSchema nvarchar(max)
    DECLARE @RelatedTable nvarchar(max)

    -- WWB: Select all tables that the input table is dependent on
    DECLARE table_cursor CURSOR LOCAL  FOR
          SELECT PK_Schema, PK_Table
          FROM #TableRelationships
          WHERE FK_Schema = @Schmea AND FK_Table = @Table

    OPEN table_cursor;

    -- Perform the first fetch.
    FETCH NEXT FROM table_cursor INTO @RelatedSchema, @RelatedTable;

    -- Check @@FETCH_STATUS to see if there are any more rows to fetch.
    WHILE @@FETCH_STATUS = 0
    BEGIN

        -- WWB: If We have Recurred To Where We Start This
        -- Is a Circular Reference
        -- Begin failing out of the recursions
        IF (@BaseSchmea = @RelatedSchema AND 
                @BaseTable = @RelatedTable)
            BEGIN
                SET @Fail = @RelatedSchema + '.' + @RelatedTable
                RETURN
            END
        ELSE            
        BEGIN

            DECLARE @Count int

            -- WWB: Check to make sure that the dependencies are not in the stack
            -- If they are we don't need to go down this branch
            SELECT    @Count = COUNT(1)
            FROM    #Stack    
            WHERE    #Stack.[Schema] = @RelatedSchema AND 
                #Stack.[Table] = @RelatedTable

            IF (@Count=0) 
            BEGIN
                -- WWB: Recurse
                EXECUTE SqlAzureRecursiveFind @BaseSchmea, 
                    @BaseTable, 
                    @RelatedSchema, @RelatedTable, @Fail OUTPUT
                IF (LEN(@Fail) > 0)
                BEGIN
                    -- WWB: If the Call Fails, Build the Output Up
                    SET @Fail = @RelatedSchema + '.' + @RelatedTable 
                        + ' -> ' + @Fail
                    RETURN
                END
            END
       END

       -- This is executed as long as the previous fetch succeeds.
    FETCH NEXT FROM table_cursor INTO @RelatedSchema, @RelatedTable;
    END

    CLOSE table_cursor;
    DEALLOCATE table_cursor;    

GO    

SET NOCOUNT ON

DECLARE @Schema nvarchar(max)
DECLARE @Table nvarchar(max)
DECLARE @Fail nvarchar(max)

-- WWB: Loop Through All the Tables In the Database Checking Each One
DECLARE list_cursor CURSOR FOR
      SELECT [Schema], [Table]
      FROM #TableList

OPEN list_cursor;

-- Perform the first fetch.
FETCH NEXT FROM list_cursor INTO @Schema, @Table;

-- Check @@FETCH_STATUS to see if there are any more rows to fetch.
WHILE @@FETCH_STATUS = 0
BEGIN

    -- WWB: Clear the Stack (Don't you love Global Variables)
    DELETE #Stack

    -- WWB: Initialize the Input
    SET @Fail = ''

    -- WWB: Check the Table
    EXECUTE SqlAzureRecursiveFind @Schema, 
        @Table, @Schema,
         @Table, @Fail OUTPUT
    IF (LEN(@Fail) > 0)
    BEGIN
        -- WWB: Failed, Output
        SET @Fail = @Schema + '.' + @Table + ' -> ' + @Fail
        PRINT @Fail
    END

   -- This is executed as long as the previous fetch succeeds.
    FETCH NEXT FROM list_cursor INTO @Schema, @Table;
END

-- WWB: Clean Up
CLOSE list_cursor;
DEALLOCATE list_cursor;    

DROP TABLE #TableRelationships
DROP TABLE #Stack
DROP TABLE #TableList
DROP PROC SqlAzureRecursiveFind
Run Code Online (Sandbox Code Playgroud)