【发布时间】:2014-09-29 17:43:18
【问题描述】:
我在 SQL Server 数据库中有两个表,它们是多对多关系,TableA 中的行代表我的业务逻辑中的“容器”结构,TableB 中的“子”对象可以是包含在任意数量的这些容器中。我创建了一个链接表TableA_X_TableB,它由以下列组成:
TableA_PK UNIQUEIDENTIFIER, TableB_PK INT, Sequence INT
... 最后一列用于记录 TableA 的“容器”中的 TableB 项目的序列——因为这些确实需要有序列表。
我所有的 CRUD 都非常简单,除了这个:当我从列表的中间删除一个项目时,我希望 SQL Server 在与特定 TableA 序列相关的序列号中“填补空白” .即,如果我有六个与特定 TableA_PK 关联的条目......
TableA_PK | TableB_PK | Sequence |
=============================================================
AD7D5099-A14D-48D4-9860-6578EDF7C006 | 10389 | 0 |
AD7D5099-A14D-48D4-9860-6578EDF7C006 | 9368 | 1 |
AD7D5099-A14D-48D4-9860-6578EDF7C006 | 9537 | 2 |
AD7D5099-A14D-48D4-9860-6578EDF7C006 | 18499 | 3 |
AD7D5099-A14D-48D4-9860-6578EDF7C006 | 15759 | 4 |
AD7D5099-A14D-48D4-9860-6578EDF7C006 | 5872 | 5 |
……然后执行:
DELETE TableA_X_TableB
WHERE TableA_PK = 'AD7D5099-A14D-48D4-9860-6578EDF7C006'
AND TableB_PK = 9537
…我希望序列值读取0 1 2 3 4,而不是0 1 3 4 5。
我已经尝试了很多方法。我不会全部列出来,但作为一个例子,在许多失败的实验之后似乎最有可能发生的事情(而且也失败了,要清楚!):
DECLARE @seq INT
SET @seq = -1;
WITH listEntries AS (
SELECT TOP 100 PERCENT *
FROM TableA_X_TableB
WHERE TableA_PK = 'AD7D5099-A14D-48D4-9860-6578EDF7C006'
ORDER BY Sequence ASC)
UPDATE listEntries
SET @seq = listEntries.Sequence = @seq + 1
FROM listEntries;
这会放入一个更新的、数字正确的序列号列表,好吧……但基于内部数据库顺序,而不是基于前一个序列号的排序。结果是用户仔细排序的列表在删除项目的那一刻变得混乱。
我可以想到几个解决方法,但是
- 令我震惊的是,在 T-SQL 中应该有一种相当优雅的方式来执行此操作;
- 这个问题以前出现过,而且很可能会再次出现。我宁愿以“正确”的方式解决它,也不愿继续采用同样精心设计的技巧。
谢谢!
更新 ======================================== ================
一个难题是这个表也有几个索引。一个是为了确保TableA_PK 和TableB_PK 的任意组合是唯一的,另外几个是为了加快跨大数据集的一些连接。如果没有索引,我上面的示例代码可以正常工作,但是一旦它们到位,我天真的 ORDER BY 子句总是会被它们覆盖。 (这里可能有一个解决方案,但接受的解决方案要优雅得多。)
此外,正如我所怀疑的,在总体要求中存在某些边缘情况,其中允许序列间隙——但前提是并且仅在最终用户这样说的情况下。我无法看到一个直接的基于 ROW_NUMBER() 的解决方案可以解决这个问题; @jpw 下面聪明而简洁的“配方”不仅解决了最初的问题,而且通过一些调整还允许在必要时进行局部重新排序。
【问题讨论】:
-
如果序列号与该行中的实际值完全无关,您为什么还要费心存储序列号?只需在运行时确定数字(例如使用
ROW_NUMBER()),而不是尝试在数据库中维护这些无意义的数据。在有人真正查询他们运行查询时实际存在的数据之前,这真的没有任何意义,对吧?那么,为什么要竭尽全力将存储的数据保存在一些神奇的、无缝的必杀技中呢? -
你也应该搜索并阅读“古怪更新”——恕我直言,这不是一个好主意。
-
古怪的更新肯定有两个阵营的人。在您尝试之前,有一些非常严格的要求。仅这一点就足以让许多人回避它。随着 LEAD 和 LAG 的加入,它似乎真的不像以前那样需要了。
-
@AaronBertrand: ROW_NUMBER() 等人给了我数据库中的项目序列,无论是“自然”顺序还是基于某种 ORDER BY——我应该更多地强调我的声明,这些“需要待排序列表”……实际上是用户排序的。 “序列”列不是无意义的数据,它是用户之前在富客户端应用程序中拖动项目的顺序。
标签: sql-server tsql sql-update sequence sql-delete