2012-03-21 28 views
15

The MSDN claims that the order is什么是数据集插入/删除/修改的正确顺序?

  1. 子表:删除记录。
  2. 父表:插入,更新和删除记录。
  3. 子表:插入和更新记录。

我有一个问题。

例:ParentTable有两个记录parent1(ID:1)和parent2(ID:2)

ChildTable有记录child1(ID:1的ParentId:1)

如果我们更新的child1有一个新的父亲2,然后我们删除父亲1。

  1. 我们有没有在子表
  2. 我们删除parent1删除:我们打破了条件,但因为孩子还连接到parent1,除非我们先对其进行更新。

那么什么是正确的顺序,并且是关于这个主题的MSDN错误?

我personnals的想法是

  1. 子表:删除记录。
  2. 父表:插入,更新记录。
  3. 子表:插入和更新记录。
  4. 父表:删除记录。

但问题是,由于潜在的唯一约束,我们必须在添加新的表之前总是删除表中的记录......所以我现在没有解决方案来将我的数据提交到我的数据库。

编辑:谢谢你的答案,但你的角落的情况是我每天的情况下......我选择了丑陋的解决方案,以禁用约束,然后更新数据库,并重新启用约束。我还在寻找一个更好的解决方案..

+0

这是MS SQL服务器?或者我错过了标签。有趣的问题+1 – cctan 2012-03-21 09:54:26

+0

这是SqlServer 2008,但我认为它是漂亮的DBMS独立! – 2012-03-21 10:01:15

+0

我认为改变父母的孩子是一种不寻常的情况。 – 2012-03-21 10:25:07

回答

4

您的SQL产品不支持延迟约束检查吗?

如果没有,你可以尝试

删除所有子记录 - 删除所有父记录 - 插入所有父记录 - 插入所有子记录

其中任何更新已拆分成它们的组成删除和插入。

这应该在所有情况下都能正常工作,但速度可以接受,但可能不会...

也可证明,这仅仅是可以在所有的情况下正常工作,因为方案:

(一)对父键约束决定了家长DELETES必须先父插入,
(二)关键对孩子的约束决定了孩子DELETES必须先于孩子的插入,
(C)FK决定了孩子DELETES必须先父DELETES
(d)FK也决定了孩子的INSERT必须遵循父INSERTS

给定的顺序是只有可能满足的一个ies这4个要求,并且它还表明UPDATE给孩子无论如何都无法解决问题,因为UPDATE意味着“同时”DELETE加INSERT。

+0

非常有趣的一点,我从来没有看到更新作为插入+更新,但知道我明白为什么我处于死胡同。对于性能,我更喜欢禁用约束,但我坚持将更新分为两部分。 – 2012-03-28 07:11:11

+0

感谢您的回答!我现在正在努力解决类似的问题。你的建议可能适用于我,但用DELETE和INSERT替换UPDATE可能会产生不必要的副作用,例如级联删除某些孩子。自从你回答了这个问题之后,你有什么新想法吗? – suryadeva 2018-02-01 13:45:57

3

听起来好像:

  1. 插入parent2。孩子仍然指向parent1。
  2. 将孩子更新为指向parent2。现在没有引用parent1。
  3. 删除parent1。

您希望将其包装在可用的交易中。

根据您的方案,你也可以此扩大到:

  1. 更新parent1,表明它已被锁定(或DB锁定),从而防止更新。
  2. 插入parent2
  3. 更新儿童指出parent2
  4. 删除parent1

这项命令的优点是家长和孩子之间的连接将返回贯穿一致的结果。当孩子更新时,连接的结果将“翻转”到新的状态。

编辑:

另一种选择是父/子引用移动到另一台,例如“链接”;

CREATE TABLE links (
    link_id INT NOT NULL IDENTITY(1,1) PRIMARY KEY, 
    parent_id INT NOT NULL, 
    child_id INT NOT NULL 
); 

你可能想要父键和子列的外键约束,当然有一些合适的索引。这种安排允许父表和子表之间非常灵活的关系 - 可能过于灵活,但这取决于您的应用程序。现在你可以做一些类似的事情;

UPDATE links 
    SET parent_id = @new_parent_id 
    WHERE parent_id = @old_parent_id 
    AND child_id = @child_id; 
0

我认为在表上分隔条件的动作是不是一个很好的设计,所以我的解决办法是

  1. 插入/更新/删除父表
  2. 插入/更新/删除子表

关键是你不应该改变一个子记录的parentId,你应该删除parent1的子项并且添加一个新的子项给parent2。通过这样做,你将不再担心破损约束。当然你必须使用交易。

+0

想象一下用户表,指向国家表。我不能删除用户行,每次用户想改变他的国家......(不要回答我,我不会删除国家,这不是重点:)) – 2012-03-21 11:09:36

+0

我认为你不需要在更改用户国家时更新国家/地区表。所以你不需要删除用户:)。这种情况只发生在我的主子表上,而不是查找表。 – 2012-03-21 11:24:39

1

需要在不删除子记录的情况下删除父记录是非常不寻常的,我确定MS定义的数据集操作通常规定的顺序不适用于这种情况。

最有效的方法是更新子记录以反映新父项,然后删除原始父项。正如其他人所说的,这个操作应该在一个事务中执行。

4

你必须考虑到他们的背景。 MS说

当一个数据集更新相关的表,它更新 按照正确的顺序,以减少侵犯引用 完整性约束的机会是很重要的。

在编写客户端数据应用软件的上下文中。

为什么减少违反参照完整性约束的可能性很重要?由于违反了相应限制意味着数据库管理系统和客户端之间

  • 更多往返,无论是客户端的代码来处理违反约束,或者为人类用户办理侵犯,
  • 更多的时间考虑,
  • 更多的负载在服务器上,人为错误
  • 更多的机会,
  • 更多的机会为并发更新改变的基础数据(可能会混淆的任一应用程序代码,人类用户,或两者)。

为什么他们认为他们的程序正确吗?因为它提供了一个单一的过程,可以避免几乎所有常见情况下的参照完整性违规,甚至在很多不常见的情况下。例如 。 。 。

  • 如果更新是在参考表中的删除操作,而如果引用表中的外键被声明为ON DELETE CASCADE,那么最优的事情是简单地删除引用的行(父行) ,并让dbms管理级联。 (这也是ON DELETE SET DEFAULT和ON DELETE SET NULL的最佳选择。)

  • 如果更新是对引用表的DELETE操作,并且引用表中的外键声明为ON DELETE RESTRICT,那么最好的办法是首先删除所有的引用行(子行),然后删除引用的行。

但是,通过正确使用事务,MS的过程使数据库保持一致的状态,无论如何。它的价值在于,它是一个单一的客户端进程,用于编码和维护,尽管在所有情况下都不是最佳的。 (这是通常在软件设计的情况 - 选择这不是在所有情况下的最佳方式单一的ActiveRecord跃居头脑。)

你说

例:ParentTable有两个记录parent1(ID:1 )和parent2(同上 :2)

ChildTable有记录child1(ID:1的ParentId:1)

如果我们更新的child1有一个新的父parent2,和我们 删除parent1。

  1. 我们有没有在子表
  2. 我们删除parent1删除:我们打破了条件,但因为孩子还连接到parent1,除非我们先对其进行更新。

这不是一个参照完整性问题;这是一个程序问题。这个问题显然需要两个交易。

  1. 更新孩子有一个新的父母,然后提交。无论第一个父母发生什么情况,都必须更正此数据。具体来说,即使存在并发更新或其他约束条件,这些数据必须被纠正,这会使其暂时或永久不可能删除第一个父级。 (这不是一个参照完整性问题,因为在SQL外键约束中没有ON DELETE SET TO NEXT PARENT ID或MAKE YOUR BEST GUESS子句。)

  2. 删除第一个父代,然后提交。这可能需要首先更新许多表中的任意数量的子行。在一个庞大的组织中,我可以想象一些像这样的删除需要几周才能完成。

+0

希望我可以给这个不止一个upvote! – user158017 2012-03-24 14:19:41

0

MSDN声明在使用依赖关系(外键)的基础上是正确的。该订单视为

  1. 子表(级联删除)
  2. 父表:插入和/或更新和/或删除记录意味着级联删除的最后一步。
  3. 子表:插入或更新。

由于我们谈论级联删除,我们必须保证通过删除父级记录,在删除父级记录之前,需要删除与父级相关的任何子级记录。如果我们没有孩子记录,那么在儿童级别没有删除。就这样。

另一方面,你可能以不同的方式接近你的情况。我认为现实生活(几乎)情景会更有帮助。假设父表是订单的主要部分(orderID,clientID等),而子表是详细信息部分(detailID,orderID,productOrServiceID等)。所以,你会得到一个订单,则有以下

父表

orderID = 1 (auto increment) 
... 

子表

detailID = 1 (auto increment) 
orderID = 1 
productOrServiceID = 342 

and 

detailID = 2 
orderID = 1 
productOrServiceID = 169 

and 

detailID = 3 
orderID = 1 
productOrServiceID = 307 

因此,我们有三个产品/服务的一个订单。现在您的客户希望您将第二个产品或服务转移到新的订单,并在稍后交付。你有两个选择来做到这一点。

第一个(直接)

  • 创建一个全新的秩序,通过设置得的orderID = 2

  • 更新子表(新父记录)的orderID = 2,其中单编号= 1和productOrServiceID = 169

因此,您将有

父表

orderID = 1 (auto increment) 
... 

and 

orderID = 2 
... 

子表

detailID = 1 (auto increment) 
orderID = 1 
productOrServiceID = 342 

and 

detailID = 2 
orderID = 2 
productOrServiceID = 169 

and 

detailID = 3 
orderID = 1 
productOrServiceID = 307 

第二个(间接)

  • 从子表保持第二产品/服务的一个DataRow作为变量

  • 从子表中删除相关行

  • 创建一个全新的秩序,得到单编号(新父记录)= 2

  • 通过改变从1场的orderID 2

结果插入子表上的不停的DataRow你将有

父表

orderID = 1 (auto increment) 
... 

and 

orderID = 2 
... 

子表

detailID = 1 (auto increment) 
orderID = 1 
productOrServiceID = 342 

and 

detailID = 3 
orderID = 1 
productOrServiceID = 307 

and 

detailID = 4 
orderID = 2 
productOrServiceID = 169 

的原因第二选项,这是通过的方式,优选一个用于许多应用中,是给每个父记录细节id的原始序列。我已经看到了通过重新创建所有细节记录来扩展第二个选项的例子。我认为找到与这种情况相关的开源解决方案并检查实现是非常容易的。

最后,我的个人建议是避免使用数据集来做这种事情,除非您的应用程序是单用户。数据库可以通过事务以线程安全的方式轻松处理这个“问题”。

相关问题