我在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;
这会输入一个更新的,数字正确的序列号列表,好吧......但是基于内部数据库顺序,而不是前一个序列号的排序。结果是用户仔细排序的列表在项目被淘汰的那一刻变得混乱。
我可以想到几个解决方法,但是
谢谢!
更新 ======================================= ================
一个难题是这张表还有几个索引;一个用于确保TableA_PK
和TableB_PK
的任意组合是唯一的,还有一些用于加速跨大量数据集的某些连接。没有索引,上面的示例代码工作正常,但是一旦它们到位,我的天真ORDER BY子句将始终被它们覆盖。 (这里可能有一个解决方案,但接受的解决方案更优雅。)
此外,正如我所怀疑的那样,总体要求中存在某些边缘情况,其中序列间隙是允许的 - 但仅限于最终用户这样说的情况。我无法看到一个直接的基于ROW_NUMBER()的解决方案可以解决这个问题; @jpw下面的巧妙和简洁的“配方”不仅解决了原始问题,而且通过一些调整也允许在必要时进行本地重新排序。
答案 0 :(得分:1)
也许使用row_number()分区的TableA_PK按顺序排序,就像这样可以工作:
UPDATE Table1 SET Sequence = rn
FROM Table1
INNER JOIN (
SELECT
[TableA_PK],
[TableB_PK] ,
rn= ROW_NUMBER() OVER (PARTITION BY tablea_pk ORDER BY tablea_pk, sequence) -1
FROM Table1
WHERE TableA_PK = 'AD7D5099-A14D-48D4-9860-6578EDF7C006'
) derived ON table1.TableA_PK = derived.TableA_PK and Table1.TableB_PK = derived.TableB_PK
删除和更新前后显示