我使用的不是Oracle或Postgresql的数据库,这意味着我无法访问延迟约束,这意味着约束必须始终有效(而不是仅仅在提交时)。
假设我将链表类型结构存储在数据库中,如下所示:
id parentId
---------------
1 null
2 1
3 2
4 3
5 4
6 5
parentId
是id
的外键引用,并且必须通过约束才是唯一的。
假设我想将项目5
移到项目1
之前,所以我们的数据库看起来像这样:
id parentId
---------------
1 null
2 5 <-- different
3 2
4 3
5 1 <-- different
6 4 <-- different
需要更改三行,即三个更新语句。这些更新语句中的任何一个都会导致约束违规:在约束再次生效之前,所有三个语句都必须完成。
我的问题是:不违反唯一性约束的最佳方式是什么?
我现在可以设想两种不同的解决方案,我都不喜欢:
parentId
设置为null
,然后执行三次更新答案 0 :(得分:0)
您可以在单个查询中执行此操作。我确信这有很多变化,但这是我会用的......
DECLARE
@node_id INT,
@new_parent_id INT
SELECT
@node_id = 5,
@new_parent = 1
UPDATE
yourTable
SET
parent_id = CASE WHEN yourTable.id = target_node.id THEN new_antiscendant.id
WHEN yourTable.id = descendant.id THEN target_node.parent_id
WHEN yourTable.id = new_descendant.id THEN target_node.id
END
FROM
yourTable AS target_node
LEFT JOIN
yourTable AS descendant
ON descendant.parent_id = target_node.id
LEFT JOIN
yourTable AS new_antiscendant
ON new_antiscendant.id = @new_parent_id
LEFT JOIN
yourTable AS new_descendant
ON COALESCE(new_descendant.parent_id, -1) = COALESCE(new_antiscendant.id, -1)
INNER JOIN
yourTable
ON yourTable.id IN (target_node.id, descendant.id, new_descendant.id)
WHERE
target_node.id = @node_id
即使@new_parent_id为NULL或列表中的最后一条记录,这也会有效。
MySQL不喜欢更新中的自连接,因此该方法可能是将LEFT JOIN转换为临时表以获取新映射。然后加入该表以在单个查询中更新所有三个recor。
INSERT INTO
yourTempTable
SELECT
yourTable.id AS node_id,
CASE WHEN yourTable.id = target_node.id THEN new_antiscendant.id
WHEN yourTable.id = descendant.id THEN target_node.parent_id
WHEN yourTable.id = new_descendant.id THEN target_node.id
END AS new_parent_id
FROM
yourTable AS target_node
LEFT JOIN
yourTable AS descendant
ON descendant.parent_id = target_node.id
LEFT JOIN
yourTable AS new_antiscendant
ON new_antiscendant.id = @new_parent_id
LEFT JOIN
yourTable AS new_descendant
ON COALESCE(new_descendant.parent_id, -1) = COALESCE(new_antiscendant.id, -1)
INNER JOIN
yourTable
ON yourTable.id IN (target_node.id, descendant.id, new_descendant.id)
WHERE
target_node.id = @node_id
UPDATE
yourTable
SET
parent_id = yourTempTable.newParentID
FROM
yourTable
INNER JOIN
yourTempTable
ON yourTempTamp.node_id = yourTable.id
(确切的语法取决于您的RDBMS。)