是否可以遍历我的 SQL 数据库中的每个 table 并找到其他行中未使用的记录? (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)
我想编写一个 SQL 脚本来指出我的整个数据库中的哪些记录没有被其他 table 用作参考。
例如,如果我有一个“评论”table,我想找出我的table中哪些记录没有在 Users、nor 和 Posts 中有关系条目,但没有指定这些 tables按名字。
类似于:
'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'
我知道如何执行 'IF NOT FOUND' 和 'DELETE' 部分,但我很难找到前三个步骤。
欢迎任何想法 and/or 有用的提示或链接。
我很高兴分享我的脚本作为对此 post 完成后的评论,因为我认为它对许多 SQL 开发人员有用。
我想到了自己写脚本,这里是(使用动态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 脚本来指出我的整个数据库中的哪些记录没有被其他 table 用作参考。
例如,如果我有一个“评论”table,我想找出我的table中哪些记录没有在 Users、nor 和 Posts 中有关系条目,但没有指定这些 tables按名字。 类似于:
'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'
我知道如何执行 'IF NOT FOUND' 和 'DELETE' 部分,但我很难找到前三个步骤。 欢迎任何想法 and/or 有用的提示或链接。 我很高兴分享我的脚本作为对此 post 完成后的评论,因为我认为它对许多 SQL 开发人员有用。
我想到了自己写脚本,这里是(使用动态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
希望对其他人有帮助:-)