[英]Constraint for one-to-many relationship
We have a two tables with a one-to-many relationship. 我们有两个表与一对多的关系。 We would like to enforce a constraint that at least one child record exist for a given parent record.
我们希望强制执行约束,即给定父记录至少存在一个子记录。
Is this possible? 这可能吗?
If not, would you change the schema a bit more complex to support such a constraint? 如果没有,您是否会更改模式以支持此类约束? If so how would you do it?
如果是这样你会怎么做?
Edit: I'm using SQL Server 2005 编辑:我正在使用SQL Server 2005
Such a constraint isn't possible from a schema perspective, because you run into a "chicken or the egg" type of scenario. 从架构的角度来看,这样的约束是不可能的,因为你遇到了“鸡或鸡蛋”类型的场景。 Under this sort of scenario, when I insert into the parent table I have to have a row in the child table, but I can't have a row in the child table until there's a row in the parent table.
在这种情况下,当我插入父表时,我必须在子表中有一行,但是在父表中有一行之前,我不能在子表中有一行。
This is something better enforced client-side. 这是更好的强制客户端。
如果您的后端支持可延迟约束,PostgreSQL也是如此。
This isn't really something 'better enforced on the client side' so much as it is something that is impractical to enforce within certain database implementations. 这实际上并不是“在客户端更好地执行”,而是在某些数据库实现中强制实施的东西。 Realistically the job DOES belong in the database and at least one of the workarounds below should work.
实际上,作业属于数据库,并且下面的解决方法中至少有一个应该有效。
Ultimately what you want is to constrain the parent to a child. 最终你想要的是将父母约束为孩子。 This guarantees that a child exists.
这保证了孩子的存在。 Unfortunately this causes a chicken-egg problem because the children must point to the same parent causing a constraint conflict.
不幸的是,这会导致鸡蛋问题,因为孩子必须指向导致约束冲突的同一父母。
Getting around the problem without visible side-effects in the rest of your system requires one of two abilities - neither of which is found in SQL Server. 在系统的其余部分中解决问题而没有可见的副作用需要两种能力中的一种 - 在SQL Server中都找不到这两种能力。
1) Deferred constraint validation - This causes constraints to be validated at the end the transaction. 1)延迟约束验证 - 这会导致在事务结束时验证约束。 Normally they happen at the end of a statement.
通常它们发生在声明的最后。 This is the root of the chicken-egg problem since it prevents you from inserting either the first child or the parent row for lack of the other and this resolves it.
这是鸡蛋问题的根源,因为它会阻止你插入第一个孩子或父行而缺少另一个,这就解决了它。
2) You can use a CTE to insert the first child where the CTE hangs off of the statement that inserts the parent (or vise versa). 2)您可以使用CTE插入CTE挂起插入父节点的语句的第一个子节点(反之亦然)。 This inserts both rows in the same statement causing an effect similar to deferred constraint validation.
这会在同一语句中插入两行,从而产生类似于延迟约束验证的效果。
3) Without either you have no choice but to allow nulls in one of the references so you can insert that row without the dependency check. 3)没有任何一个,你别无选择,只能在其中一个引用中允许空值,这样你就可以在没有依赖性检查的情况下插入该行。 Then you must go back and update the null with the reference to the second row.
然后,您必须返回并使用对第二行的引用更新null。 If you use this technique you need to be careful to make the rest of the system refer to the parent table thru a view that hides all rows with null in the child reference column.
如果您使用此技术,则需要注意使系统的其余部分通过一个视图来引用父表,该视图在子引用列中隐藏所有具有null的行。
In any case your deletes of children are just as complicated because you cannot delete the child that proves at least one exists unless you update the parent first to point to a child that won't be deleted. 在任何情况下,您删除的子项都同样复杂,因为您无法删除证明至少存在一个子项的子项,除非您首先更新父项以指向不会被删除的子项。
When you are about to delete the last child either you must throw an error or delete the parent at the same time. 当您要删除最后一个子项时,您必须同时抛出错误或删除父项。 The error will occur automatically if you don't set the parent pointer to null first (or defer validation).
如果您没有先将父指针设置为null(或延迟验证),则会自动发生错误。 If you do defer (or set the child pointer to null) your delete of the child will be possible and the parent can then be deleted as well.
如果您确实推迟(或将子指针设置为null),则可以删除子项,然后也可以删除父项。
I literally researched this for years and I watch every version of SQL Server for relief from this problem since it's so common. 我多年来一直在研究这个问题,我看每个版本的SQL Server都可以解决这个问题,因为它很常见。
PLEASE As soon as anyone has a practical solution please post! 请尽快有人提供实用的解决方案,请发布!
PS You need to either use a compound key when referring to your proof-of-child row from the parent or a trigger to insure that the child providing proof actually considers that row to be its parent. PS您需要在引用来自父项的子证明行时使用复合键,或者触发以确保提供证据的孩子实际上认为该行是其父项。
PPS Although it's true that null should never be visible to the rest of your system if you do both inserts and the update in the same transaction this relies on behavior that could fail. PPS虽然如果你在同一个事务中同时执行插入和更新,那么对于系统的其余部分来说,null永远不应该是可见的,这依赖于可能失败的行为。 The point of the constraint is to insure that a logic failure won't leave your database in an invalid state.
约束的关键是确保逻辑故障不会使数据库处于无效状态。 By protecting the table with a view that hides nulls any illegal row will not be visible.
通过使用隐藏空值的视图保护表,任何非法行都将不可见。 Clearly your insert logic must account for the possibility that such a row can exist but it needs inside knowledge anyway and nothing else needs to know.
很明显,你的插入逻辑必须考虑到这样一行可能存在的可能性,但无论如何它都需要内部知识,而其他任何事情都不需要知道。
I am encountering this issue, and have a solution implemented in Oracle rel.11.2.4. 我遇到了这个问题,并在Oracle rel.11.2.4中实现了一个解决方案。
Create a function which accepts a parent PK, and returns a COUNT of children for that PK. 创建一个接受父PK的函数,并为该PK返回COUNT个子项。 -- I ensure that NO_DATA_FOUND exceptions return 0.
- 我确保NO_DATA_FOUND异常返回0。
Create a virtual column CHILD_COUNT
on the parent table and calculate it to the function result. 在父表上创建一个虚拟列
CHILD_COUNT
,并将其计算为函数结果。
Create a deferrable CHECK constraint on the CHILD_COUNT
virtual column with the criteria of CHILD_COUNT > 0
在
CHILD_COUNT
虚拟列上创建可延迟的CHECK约束,条件为CHILD_COUNT > 0
It works as follows: 它的工作原理如下:
CHILD_COUNT > 0
CHECK constraint fails and the transaction rolls back. CHILD_COUNT > 0
CHECK约束将失败,并且事务将回滚。 COMMIT
is issued. COMMIT
时满足所有完整性约束。 CHILD_COUNT
virtual column is recalculated on COMMIT
and no integrity violation occurs. COMMIT
上重新计算CHILD_COUNT
虚拟列,并且不会发生完整性冲突。 CHILD_COUNT
check constraint will violate when the transaction commits. CHILD_COUNT
检查约束。 NOTE: that I would not need a virtual column if Oracle would allow user-function-based CHECK constraints
at rel.11.2.4. 注意:如果Oracle在rel.11.2.4中允许基于用户功能的
CHECK constraints
,那么我就不需要虚拟列。
How about a simple non nullable column? 一个简单的非可空列怎么样?
Create Table ParentTable
(
ParentID
ChildID not null,
Primary Key (ParentID),
Foreign Key (ChildID ) references Childtable (ChildID));
)
If your business logic allows and you have default values you can query from the database for each new parent record, you can then use a before insert trigger
on the parent table to populate the non nullable child column. 如果您的业务逻辑允许并且您具有默认值,则可以从数据库中查询每个新父记录,然后可以在父表上使用
before insert trigger
来填充非可空子列。
CREATE or REPLACE TRIGGER trigger_name
BEFORE INSERT
ON ParentTable
FOR EACH ROW
BEGIN
-- ( insert new row into ChildTable )
-- update childID column in ParentTable
END;
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.