删除失踪父母的子记录

问题描述 投票:0回答:5

我正在尝试创建一个 SQL 查询来在删除父记录时删除子记录。
问题是,孩子和父母都存储在同一个表中。
这是(简化的)数据模型:

事物表

    ID  |  Name  | ParentID
   1000 | Thing1 |   NULL
   1001 | Thing2 |   1000
   1002 | Thing3 |   1000
   1003 | Thing4 |   1000
   1004 | Thing5 |   1003

ChildThingTable

    ID  |  Color  
   1001 |  Blue
   1002 |  Black
   1003 |  Green
   1004 |  Red  

假设ID 1000(父级)被删除,我需要从ChildThingTable和ThingTable中删除相应的记录。

我唯一的限制是我不能使用触发器或以任何方式更改底层数据库结构。

这是我已经制定出的伪代码,但我很难将其转换为 SQL:

  1. 根据 ID 和 ParentID,从 ChildThingTable 中删除 ThingTable 中匹配的记录没有父项的记录。
  2. 从 ThingTable 中删除匹配记录没有父级的记录。

如有任何帮助,我们将不胜感激!

sql sql-server sql-server-2008
5个回答
0
投票

您可以使用通用表表达式来递归

-- Begin Create Test Data

SET NOCOUNT ON 

CREATE TABLE #ThingTable (
ID INT NOT NULL,
[Name] VARCHAR(255) NOT NULL,
[ParentID] INT NULL

)


CREATE TABLE #ChildThingTable (
ID INT NOT NULL,
[Color] VARCHAR(255) NOT NULL,

)

INSERT INTO #ThingTable (ID,[Name],ParentID) VALUES (1000,'Thing1',NULL)
INSERT INTO #ThingTable (ID,[Name],ParentID) VALUES (1001,'Thing2',1000)
INSERT INTO #ThingTable (ID,[Name],ParentID) VALUES (1002,'Thing3',1000)
INSERT INTO #ThingTable (ID,[Name],ParentID) VALUES (1003,'Thing4',1000)
INSERT INTO #ThingTable (ID,[Name],ParentID) VALUES (1004,'Thing5',1003)

INSERT INTO #ChildThingTable ( ID, Color ) VALUES  ( 1001 , 'Blue')
INSERT INTO #ChildThingTable ( ID, Color ) VALUES  ( 1002 , 'Black')
INSERT INTO #ChildThingTable ( ID, Color ) VALUES  ( 1003 , 'Green')
INSERT INTO #ChildThingTable ( ID, Color ) VALUES  ( 1004 , 'Red')

SET NOCOUNT OFF
GO
-- End Create Test Data

-- This is a batch, but could easily be a stored procedure.  
DECLARE @InputID INT 
SET @InputID = 1000;

SET NOCOUNT ON
DECLARE @Temp TABLE(ID INT NOT NULL);

WITH ThingCTE (ID, ParentID, [Level])
AS
(
SELECT tt1.ID, tt1.ParentID, 1 AS [Level]
FROM #ThingTable tt1
WHERE tt1.ID = @InputID
UNION ALL
SELECT tt2.ID, tt2.ParentID, tc1.[Level]+1
FROM #ThingTable tt2
JOIN ThingCTE tc1 ON (tt2.ParentID = tc1.ID)
)
INSERT INTO @Temp
        ( ID )
SELECT ID
FROM ThingCTE

SET NOCOUNT OFF

DELETE ctt
-- Output is for debug purposes, should be commented out in production.
OUTPUT Deleted.* 
FROM #ChildThingTable ctt
JOIN @Temp t ON (ctt.ID = t.ID);

DELETE tt 
-- Output is for debug purposes, should be commented out in production.
OUTPUT Deleted.* 
FROM #ThingTable tt
JOIN @Temp t ON (tt.ID = t.ID)

DROP TABLE #ChildThingTable;
DROP TABLE #ThingTable;

0
投票

添加约束是否构成“以任何方式改变底层数据库结构”

因为我会考虑使用带有级联删除的约束,这将要求 Parent Id 列作为 ID 表的外键,然后在删除主键时添加约束,匹配的外键也会被删除。

ALTER TABLE dbo.T2
ADD CONSTRAINT FK_T1_T2_Cascade
FOREIGN KEY (EmployeeID) REFERENCES dbo.T1(EmployeeID) ON DELETE CASCADE

还有递归查询可以做类似的事情,但我会从这个开始。

那么这样的事情应该可以工作 http://en.wikipedia.org/wiki/Hierarchical_and_recursive_queries_in_SQL

DELETE
FROM thingtable START WITH ID = 1000
CONNECT BY PRIOR ID = ParentID;

0
投票

如果我正确理解你的问题,这个程序应该可以做到......

CREATE PROCEDURE DeleteThing 
    @IdToDelete INT
AS
BEGIN

    DELETE ChildThingTable
    WHERE ID IN ( SELECT ID FROM ThingTable WHERE ParentId = @IdToDelete );

    DELETE ChildThingTable
    WHERE ID = @IdToDelete;

    DELETE ThingTable
    WHERE ParentID = @IdToDelete;

    DELETE ThingTable
    WHERE ID = @IdToDelete;

END;

您可能希望将其包装在事务中,以便整个操作要么成功,要么失败。

正如其他人所提到的,这正是您应该使用引用完整性构造(外键)的原因。它们的存在是为了帮助防止无效数据。


0
投票

尝试使用递归 cte 一直爬到根,这样每个事物不仅会显示 ParentID,还会显示父级的 ParentID 等等。

CREATE TABLE Things (
  ID INT NOT NULL,
  [Name] VARCHAR(255) NOT NULL,
  [ParentID] INT NULL
);

INSERT INTO Things (ID,[Name],ParentID)
select 1000,'Thing1',NULL union all
select 1001,'Thing2',1000 union all
select 1002,'Thing3',1000 union all
select 1003,'Thing4',1002 union all
select 1004,'Thing5',1003;

注意,我在您的数据中添加了一个级别,以表明我们可以一直到达顶部,因此 Thing5 的父级是 Thing4,Thing4 的父级是 Thing3,Thing3 的父级是 Thing1。

with
Parents( ID, Name, ParentID, GrandParentID )
as(
  select t1.ID, t1.Name, t2.ID, t2.ParentID
    from Things t1
  left join Things t2
      on t2.id = t1.ParentID
  union all
  select t1.ID, t1.Name, p.ParentID, p.GrandParentID
    from Things t1
    join Parents p
      on p.ID = t1.ParentID
   where p.ParentID is not null or p.GrandParentID is not null
)
select ID, Name, ParentID
from Parents
order by ID, ParentID desc;

这会产生这样的输出Fiddle

ID   NAME   PARENTID
==== ====== ========
1000 Thing1 (null)
1001 Thing2 1000
1002 Thing3 1000
1003 Thing4 1002
1003 Thing4 1000
1004 Thing5 1003
1004 Thing5 1002
1004 Thing5 1000

因此,如果您删除了 Thing3 (ID 1002),那么还要删除上面 ParentID 列中包含 1002 的所有内容(Thing4 和 Thing5)。

从子表中删除孤儿很简单:执行左连接回到事物表并删除所有未找到匹配的内容。


0
投票

我通常在删除父记录后从子表中删除孤立记录。

DELETE T FROM ThingTable T WHERE NOT EXISTS (SELECT NULL FROM ParentTable WHERE Id=T.ParentTableId)
DELETE C FROM ChildThingTable C WHERE NOT EXISTS (SELECT NULL FROM ThingTable WHERE Id=C.ThingTableId)
© www.soinside.com 2019 - 2024. All rights reserved.