是否可以遍历我的 SQL 数据库中的每个表并查找其他行中未使用的记录? (PK/FK检查)

Posted

技术标签:

【中文标题】是否可以遍历我的 SQL 数据库中的每个表并查找其他行中未使用的记录? (PK/FK检查)【英文标题】:Is it possible to loop over each table in my SQL database and find records that aren't used in other rows ? (PK / FK check) 【发布时间】:2020-08-26 17:14:21 【问题描述】:

我想编写一个 SQL 脚本,它可以指出我整个数据库中的哪些记录没有被其他表用作参考。

例如,如果我有一个“Comments”表,我想找出我表中的哪些记录在Users 中没有关系条目, norPosts 中,但没有按名称指定这些表。 比如:

'COLLECT ALL TABLES'
   'LOOP OVER EACH ROW IN THIS TABLE'
      'CHECK OTHER TABLES TO SEE IF THIS PARTICULAR PK_ID IS PRESENT AS A FOREIGN KEY'
         'IF FOUND NOT FOUND'
            'DELETE THIS ROW FROM THIS TABLE'

我知道如何执行“如果未找到”和“删除”部分,但我很难找到前三个步骤。 欢迎任何想法和/或有用的提示或链接。 我很高兴在这篇文章完成后将我的脚本作为评论分享,因为我认为它对许多 SQL 开发人员很有用。

【问题讨论】:

id 建议使用基于集合的方法,而不是逐行方法。您可以使用连接 (***.com/questions/3826929/…) 和 ***.com/questions/16481379/… 查找和删除行。如果您正在寻找通用逻辑,可以开发它,但老实说,最好对您感兴趣的表执行此操作。 谢谢你的回答,杰里米!我会检查你提供给我的资源:-) 亲爱的 Jeremy,我有一个表“描述”,其中包含我的约 80 个表的描述。我想找出不再使用的描述。但是,我确实明白您所说的“最好为您感兴趣的表执行此操作”的意思,但我对所有 80 个表都感兴趣。我想,如果我听从你的评论,我最好从最大的表开始,然后从那里开始围绕数据库工作。但这会花很长时间,我觉得最好写一个脚本,可以循环遍历所有的表,我不介意让它运行一整夜,因为它需要一些时间来执行。 更好的是,sql server 有内部视图来查看表和列定义mssqltips.com/sqlservertutorial/196/informationschematables 和mssqltips.com/sqlservertutorial/183/informationschemacolumns。您可以编写一些 SQL 来编写带有连接的 SQL 选择查询,然后在脚本窗口中执行这些查询 进行一些搜索,您也可能获得已定义的 FK。 dataedo.com/kb/query/sql-server/list-foreign-keys 【参考方案1】:

我想办法自己写脚本,这里是(使用动态 SQL)

我有 185 个表和约 190.000 个描述条目,在执行此脚本后,我最终得到了约 15.000 个描述行。 (问题是在更改对象的描述时创建了一个新的描述,而不是更新它。在修复了我在数据库中使用的代码中的这个错误之后)。该脚本耗时约 3 小时完成。

USE [YOUR_DATABASE]

PRINT 'BEGIN SCRIPT'


-- put all the tables in your database in a table (where we will perform a cursor on)
SELECT TABLE_NAME INTO ALL_TABLES
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_TYPE = 'BASE TABLE' AND TABLE_CATALOG='YOUR_DATABASE' 

-- delete non relevant entities in ALL_TABLES table
delete from ALL_TABLES where TABLE_NAME = 'ALL_TABLES'  -- delete the 'all_tables' table
delete from ALL_TABLES where TABLE_NAME = 'DESCRIPTION' -- your to-check table

-- hide rowcounts
set nocount on


-- Variables to use in the cursors
DECLARE 
    @tableName NVARCHAR(MAX), 
    @descriptionId   INT,
    @descriptionInUse BIT;



-- cursor to get all the entities from ALL_TABLES
DECLARE cursor_allTables CURSOR
FOR SELECT 
        TABLE_NAME
    FROM 
        dbo.ALL_TABLES;


-- cursor to get all the entities out of your to-check table ( mine was Description )
DECLARE cursor_descriptionCleanUp CURSOR
FOR SELECT 
        Id
    FROM 
        dbo.Description;



-- start iterating over descriptions
OPEN cursor_descriptionCleanUp;
FETCH NEXT FROM cursor_descriptionCleanUp INTO 
    @descriptionId;

    
WHILE @@FETCH_STATUS = 0
    BEGIN
        -- set BIT to check if description is in use
        set @descriptionInUse = 0;
        -- start iterating over ALL_TABLE rows
        OPEN cursor_allTables;
            FETCH NEXT FROM cursor_allTables INTO 
                @tableName;
            WHILE @@FETCH_STATUS = 0 AND @descriptionInUse= 0
                BEGIN
                    -- check if this table has a DescriptionId
                    IF COL_LENGTH('dbo.'+@tableName, 'DescriptionId') IS NOT NULL
                        BEGIN
                            -- check the table
                            DECLARE @sql as nvarchar(MAX);
                            DECLARE @rowCount as int; 
                            SET @sql = 'SELECT DescriptionId FROM '+ @tableName+' where DescriptionId = '+ CAST(@descriptionId as nvarchar(MAX))
                            EXEC (@sql)
                            SELECT @rowCount = @@ROWCOUNT;
                            
                            -- If EXEC (@sql) returned more than 0 rows, this Description row is in use in a table -> mark it as used 
                            IF @rowCount > 0
                                BEGIN
                                    set @descriptionInUse= 1;
                                END
                        END
                        
                    FETCH NEXT FROM cursor_allTables INTO 
                        @tableName;
                END;
            
        CLOSE cursor_allTables;
        
    --if description row is not in use -> delete this row
        IF @descriptionInUse = 0
            BEGIN
                DELETE FROM Description where Id = @descriptionId;
                PRINT 'DELETE DESCRIPTION WITH ID: ' + CAST(@descriptionId as nvarchar(MAX))
                PRINT'----------------------------------------------'
            END

            -- fetch next Description Id  from Descriptions
        FETCH NEXT FROM cursor_descriptionCleanUp INTO 
            @descriptionId;
    END;

CLOSE cursor_descriptionCleanUp;

-- clean up 
DEALLOCATE cursor_descriptionCleanUp;
DEALLOCATE cursor_allTables;
drop table ALL_TABLES
-- set nocount back to on 
set nocount off

我希望它可以帮助其他人:-)

【讨论】:

以上是关于是否可以遍历我的 SQL 数据库中的每个表并查找其他行中未使用的记录? (PK/FK检查)的主要内容,如果未能解决你的问题,请参考以下文章

SQL 连接两个表并检查两个表中的每个值是不是存在

遍历所有工作表并查找范围内的值。如果找到然后执行一些操作并转到

SQL遍历表并删除如果不是特定名称

如何连接多个表,包括查找表并按行返回数据

是否可以在同一存储过程中并行处理for循环?

连接两个表并从一列返回多个匹配项的 SQL 查询?