[英]How to cascade delete in SQL on a table that has an FK pointed to the PK of that same table?
Here's my table - ParentId
is a foreign key back to Id
. 这是我的表格
ParentId
是返回Id
的外键。 This, as you can tell, is a hierarchy: 如您所知,这是一个层次结构:
Here are my relationships (notice that under INSERT And Update Specification, the Delete and Update Rules are grayed out - this is what is causing my issue): 这是我的关系(请注意,在“插入和更新规范”下,“删除”和“更新规则”显示为灰色-这是造成我的问题的原因):
Here's my data: 这是我的数据:
And finally, just for visual purposes, here's my output on the UI: 最后,仅出于视觉目的,这是我在用户界面上的输出:
QUESTION : What I want to do is delete "Australia" and have SQL automatically cascade down and delete all "sub-regions" that are chained together by ParentId/Id. 问题 :我要删除的是“ Australia”并让SQL自动降级并删除由ParentId / Id链接在一起的所有“子区域”。 I know that I could custom code something to do this, but I want to avoid doing that for sake of time.
我知道我可以自定义代码来执行此操作,但是为了避免时间太长,我想避免这样做。 Why are the Delete and Update Rules grayed out?
为什么删除和更新规则显示为灰色? How can I Cascade Delete on a table that has a foreign key tied to the primary key within the same table?
如何在具有与同一表中的主键绑定的外键的表上层叠删除?
I'm afraid this is not possible to do in SQL Server. 恐怕在SQL Server中无法做到这一点。 If you try to create your table like this:
如果您尝试像这样创建表格:
create table Region (
Id int primary key,
ParentId int references Region(Id) on delete cascade,
Name nvarchar(50)
)
you'll receive an error: Introducing FOREIGN KEY constraint 'FK__Region__ParentId' on table 'Region' may cause cycles or multiple cascade paths
. 您会收到一个错误:
Introducing FOREIGN KEY constraint 'FK__Region__ParentId' on table 'Region' may cause cycles or multiple cascade paths
。 And, actually, it is possible to create cycles with your schema, like this: 而且,实际上,可以使用您的架构创建周期,如下所示:
Id Name ParentId
1 USA 2
2 Germany 1
So in your case I think you have to create on delete
trigger. 因此,就您而言,我认为您必须
on delete
触发器上创建。
I ended up creating a custom module that works specifically with adjacency lists. 我最终创建了一个专门用于邻接表的自定义模块。 An adjacency list in SQL is where you have a single table maintaining an "n" depth hierarchy by a column ParentId that is a foreign key back to the Id column (the primary key) in the same table.
在SQL中的邻接表中,您具有一个表,该表通过“ ParentId”列维护“ n”深度层次结构,该列是返回同一表中Id列(主键)的外键。
I created a helper class called "Children". 我创建了一个名为“儿童”的帮助程序类。 The purpose of this class is to return a list of all traversed child Id's for a given parent.
此类的目的是返回给定父级的所有遍历子ID的列表。 So if you pass in the Id for a parent that's say 6 levels up, the below code will traverse down all 6 levels and return the complete list of Id's for all children, grandchildren, great-grandchildren, and so on.
因此,如果您将Id传递给说6个级别的父级,则下面的代码将遍历所有6个级别,并返回所有孩子,孙子,曾孙等Id的完整列表。
The reason why I am getting a list of all Id's of children instead of a list of objects to delete is because if I get a list of objects to delete I would have to get these objects under a different DbContext, so when I try to actually delete them, I'll get an error saying that the object is detached (or something like that) because it was instantiated under a different context. 之所以要获得所有孩子的所有Id列表而不是要删除的对象列表,是因为如果我要删除的对象列表必须将这些对象放在不同的DbContext下,因此当我尝试实际删除它们,我将收到一条错误消息,指出该对象已分离(或类似的东西),因为它是在不同的上下文中实例化的。 Getting a list of Id's prevents this from happening and we can use the same context to perform the delete.
获取ID的列表可防止这种情况的发生,我们可以使用相同的上下文执行删除。
So, call this method GetChildrenIds(List<int> immediateChildrenIds)
from your code and pass in a list of ints
that correspond to the immediate children of a selected node. 因此,从您的代码中调用此方法
GetChildrenIds(List<int> immediateChildrenIds)
,并传入一个与所选节点的直接子代对应的ints
列表。 For example, to get the list of immediate children to pass into this method, use something like the following (this is based on an ASP.NET MVC pattern using WebAPI): 例如,要获取传递给此方法的直接子级列表,请使用类似以下的内容(它基于使用WebAPI的ASP.NET MVC模式):
// keep in mind that `id` is the `id` of the clicked on node.
// DELETE api/region/5
public HttpResponseMessage Delete(int id)
{
Region region = db.Regions.Find(id);
List<int> tempRegionIds = new List<int>();
List<int> immediateChildrenIds = (from i in db.Regions where i.ParentId == id select i.Id).ToList();
List<int> regionsToDeleteIds = new List<int>();
// the below is needed because we need to add the Id of the clicked on node to make sure it gets deleted as well
regionsToDeleteIds.Add(region.Id);
// we can't make this a static method because that would require static member
// variables, and static member variables persist throughout each recursion
HelperClasses.HandleChildren.Children GetChildren = new HelperClasses.HandleChildren.Children();
// see below this code block for the GetChildrenIds(...) method
tempRegionIds = GetChildren.GetChildrenIds(immediateChildrenIds);
// here, we're just adding to regionsToDeleteIds the children of the traversed parent
foreach (int tempRegionId in tempRegionIds)
{
regionsToDeleteIds.Add(tempRegionId);
}
// reverse the order so it goes youngest to oldest (child to grandparent)
// is it necessary? I don't know honestly. I just wanted to make sure that
// the lowest level child got deleted first (the one that didn't have any children)
regionsToDeleteIds.Reverse(0, regionsToDeleteIds.Count);
if (regionsToDeleteIds == null)
{
return Request.CreateResponse(HttpStatusCode.NotFound);
}
foreach (int regionId in regionsToDeleteIds)
{
// here we're finding the object based on the passed in Id and deleting it
Region deleteRegion = db.Regions.Find(regionId);
db.Regions.Remove(deleteRegion);
}
...
The below code is the class that returns a complete list of children Id's. 以下代码是返回孩子ID的完整列表的类。 I put this code in a separate helper class file.
我将此代码放在单独的帮助程序类文件中。 The DbContext
_db
is what I was talking about when I said you don't want to retrieve a list of objects under this context otherwise the Delete
wouldn't work when it was actually called in your controller. 我说的是DbContext
_db
,我说的是您不想在此上下文下检索对象列表,否则在控制器中实际调用Delete
时, Delete
将不起作用。 So instead, as you can see, I get a list of Id's instead and make the actual DbContext call to get the object inside my controller, not this helper class. 因此,正如您所看到的,我得到了一个Id的列表,并进行了实际的DbContext调用以将对象放入控制器内,而不是此帮助器类内。
public class Children
{
private Entities _db = new Entities(HelperClasses.DBHelper.GetConnectionString());
private List<int> _childrenIds = new List<int>();
private List<int> _childRegionIds = new List<int>();
public List<int> GetChildrenIds(List<int> immediateChildrenIds)
{
// traverse the immediate children
foreach (var i in immediateChildrenIds)
{
_childRegionIds.Add(i);
_childrenIds = (from child in _db.Regions where child.ParentId == i select child.Id).ToList();
if (_childrenIds.Any())
GetChildrenIds(_childrenIds);
else
continue;
}
return _childRegionIds;
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.