从SQL数据库表中删除未使用的父记录

时间:2013-05-06 19:28:31

标签: mysql sql sql-server oracle tsql

在SQL Server 2012中执行的测试显示,如果删除表中的单个记录将导致外键违规,则命令DELETE FROM dbo.TableName将停止并返回错误。没有记录被删除。

  • 假设未使用的记录是可以在不违反外键关系的情况下删除的记录。

  • 假设所有记录都可通过不可为空的整数ID列进行唯一标识。

  • 假设忽略可能导致副作用的功能是安全的,例如触发器或级联删除。

  • 关于表格的其他内容,应该假设其记录和关系。 (例如:解决方案在这些参数中应该是通用的。)

是否存在以下形式删除未使用的父记录的解决方案:

  1. SQL会忽略导致它的记录的错误,并成功执行不记录的记录吗?
  2. 仅在一组以某种方式检测到的未使用的父记录时执行DELETE的SQL? (对于知道FK关系的特定表格很容易完成。不太容易完成一般。)
  3. 服务器端代码,它将执行上述解决方案的等效操作?
  4. 备注:

    • SQL解决方案优于需要C#,Python,Java,Ruby等的解决方案
    • Cross-RDBMS解决方案优于RDBMS特定解决方案。

    请说明您的解决方案适用的RDBMS并且已经过测试

5 个答案:

答案 0 :(得分:0)

在这种情况下我通常做的是禁用fk约束,如果这不起作用,我通常会删除约束,清理数据,然后重新创建fk。

以下是有关如何操作的快速链接 http://www.mssqltips.com/sqlservertip/1376/disable-enable-drop-and-recreate-sql-server-foreign-keys/

答案 1 :(得分:0)

在Oracle中,您可以使用LOG ERRORS INTO选项:

示例数据设置:

create table parent (pid integer not null primary key);
create table child 
(
  cid integer not null primary key,
  pid integer not null references parent(pid)
);

insert into parent values (1);
insert into parent values (2);
insert into parent values (3);

insert into child values (1,1);
insert into child values (2,1);
insert into child values (3,2);

创建将记录所有不可删除行的表:

begin
  DBMS_ERRLOG.CREATE_ERROR_LOG('PARENT', 'ERR_LOG_PARENT');
end;
/

现在运行删除:

delete from parent
log errors into err_log_parent ('delete test')
reject limit unlimited;

SQLFiddle示例:http://sqlfiddle.com/#!4/d379c/1

DBMS_ERRLOG手册:http://docs.oracle.com/cd/B28359_01/appdev.111/b28419/d_errlog.htm
LOG ERRORS选项的手册:http://docs.oracle.com/cd/B28359_01/server.111/b28286/statements_8005.htm#CEGCHDJF

答案 2 :(得分:0)

在所有平台上都没有让你想要的神奇药丸,但你可以编写一个可以适应任何DBMS的药丸。是的,我们在SQL服务器生产数据库上完成了这项工作。这不是一个单步过程,但您可以使用动态SQL或其他方式轻松实现第二步的自动化。

重新:

  

您必须知道当前记录的每个父表。

是的,但你可以编写代表该代码的代码。使用INFORMATION_SCHEMA视图(SQL Server,MySQL)或数据库等效视图为相关表生成删除语句。然后运行您生成的语句。

一个好的起点是INFORMATION_SCHEMA.TABLES,INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS和INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE。

答案 3 :(得分:0)

如果该行具有对PK的引用,则该删除将失败但它将继续。

SET NOCOUNT ON;

DECLARE @PK varchar(10);

PRINT '-------- Delete PK --------';

DECLARE delete_cursor CURSOR FOR 
SELECT PK 
FROM PKstring
ORDER BY PK;

OPEN delete_cursor

FETCH NEXT FROM delete_cursor 
INTO @PK

WHILE @@FETCH_STATUS = 0
BEGIN
    DELETE PKstring where PK = @PK 

    FETCH NEXT FROM delete_cursor 
    INTO @PK
END 
CLOSE delete_cursor;
DEALLOCATE delete_cursor;

答案 4 :(得分:-1)

为T-SQL编写并在SQL Server 2012中测试。

此解决方案只获取表的最大ID,循环遍历每条记录并尝试删除它。它仅适用于正整数ID。如果使用否定ID,也应该运行反向循环(从0到MIN)。

DECLARE @recordID int;
DECLARE @maximumRecordID int;

SELECT @maximumRecordID = Max(recordID) from dbo.TableName;
SET @recordID = 0;

WHILE( @recordID < @maximumRecordID )
BEGIN;
    DELETE FROM dbo.TableName
    WHERE recordID = @recordID;

    SET @recordID = @recordID + 1;
END;
GO

我不一定建议任何人这样做,因为它依赖于DBMS来捕捉错误和一般的懒惰。

目前的实际应用是在我们的测试数据库中,用于正在进行的应用程序。数据库架构可以是易失性的,并由第三方控制,有时(但不是通常)使得保存脚本以供将来使用不切实际。除此之外,我不经常保存SQL脚本,因为我的大多数工作都是在C#中完成的。