简体   繁体   English

如何在FK指向同一表PK的表上用SQL级联删除?

[英]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.

相关问题 删除将PK用作FK约束的数据库表行 - Delete DB table rows in which a PK is used as a FK Constraint 删除级联上的同一个表一对多 - Same table one to many on delete cascade .Net core中查询PK表时如何获取FK表数据? - How to get FK table data when querying PK table in .Net core? 具有级联更新和删除的同一个表的多个外键 - Multiple foreign keys to same table with cascade update and delete EntityFramework CodeFirst:CASCADE DELETE用于同一个表的多对多关系 - EntityFramework CodeFirst: CASCADE DELETE for same table many-to-many relationship 如果包含PK的表已删除,则将FK更改为Null值 - Change the FK to the Null Value if the table which contains the PK was deleted EF在单个表上级联删除 - EF cascade delete on single table 在表 'XY' 上引入 FOREIGN KEY 约束 'FK_XY' 可能会导致循环或多个级联路径。 指定 ON DELETE NO ACTION - Introducing FOREIGN KEY constraint 'FK_XY' on table 'XY' may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION 桌面上的FK可能会导致多个循环或多个级联路径 - FK on table may cause multiple cycles or multple cascade paths 实体框架6如何决定是否应使用ON DELETE CASCADE定义FK - How Entity Framework 6 decides if the FK should be defined with ON DELETE CASCADE
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM