删除父项缺失的子记录

时间:2014-07-25 15:49:06

标签: sql sql-server sql-server-2008

我尝试创建SQL查询以在删除父记录时删除子记录 问题是,孩子和父母都存放在同一张桌子里 这是(简化的)数据模型:

ThingTable

    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. 从ChildThingTable中删除,其中ThingTable中的匹配记录没有父级,基于ID和ParentID。
  2. 从匹配记录中没有父级的ThingTable中删除。
  3. 非常感谢任何帮助!

4 个答案:

答案 0 :(得分:0)

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

因为我会考虑使用带有级联Delete的约束,这将要求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;

答案 1 :(得分: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;

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

正如其他人所提到的,这正是你应该使用参照完整性结构(外键)的原因。它们的存在有助于防止无效数据。

答案 2 :(得分: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;

答案 3 :(得分:0)

尝试使用递归cte一直爬到root,所以每个东西都不仅显示在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,其父级是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列(Thing4和Thing5)中删除1002的所有内容。

从子表中删除孤儿很简单:左边连接回事物表并删除所有找不到匹配项的东西。