2014-07-25 145 views
0

我试图创建一个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(父)已删除,我d需要删除相应的记录ChildThingTable以及ThingTable的Ords。

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

这里是我的伪代码,我已经制定出来,但我有困难,其转换为SQL:基于ID

  1. 从ChildThingTable删除其中ThingTable匹配的记录没有父母,和PARENTID。
  2. 从ThingTable中删除匹配记录没有父项的地方。

任何援助将不胜感激!

回答

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

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

因为我会考虑使用约束带级联删除,这将需要父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

不幸的是,我不能添加约束。我唯一的选择是使用存储过程。谢谢! – TelJanini

+0

增加了更多,它可能不完全正确,但它应该非常接近,链接应该有助于回答一些问题。 – bowlturner

+0

处理RI的正确方式就像是@bowlturner发布的。如果您尝试在存储过程中执行此操作,您可以并最终会收到错误的数据。 –

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,其母公司是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),则也与1​​002上面的PARENTID列删除一切(Thing4和Thing5) 。

从子表中删除孤立很简单:做一个左连接回到事物表并删除没有找到匹配的所有东西。