sql-server - 如何在一组行中创建行 ID
问题描述
我有一些数据,比如
Id, GroupId, Whatever
1, 1, 10
2, 1, 10
3, 1, 10
4, 2, 10
5, 2, 10
6, 3, 10
我需要添加一个“组行ID”列,例如
Id, GroupId, Whatever, GroupRowId
1, 1, 10 1
2, 1, 10 2
3, 1, 10 3
4, 2, 10 1
5, 2, 10 2
6, 3, 10 1
理想情况下,它将由数据库计算和执行。所以当我这样做的时候
INSERT INTO Foos (GroupId, Whatever) VALUES (1, 20)
我会得到正确的 GroupRowId。继续上面的示例数据,该行看起来像
Id, GroupId, Whatever, GroupRowId
7, 1, 20 4
此数据将与第 3 方共享,其中一项要求是固定这些 GroupRowId,而不管任何不同的 ORDER BY 或 WHERE 子句如何。
我已经考虑了一个视图,row_id over/partition by
但该视图在未来仍然可以修改,以破坏以前共享的数据。
我们的业务规则规定不会删除任何行,因此在这方面永远不需要重新计算 GroupRowId,并且永远不会有**缺失值。
** 在完美的商业规则世界中。
我的想法是,最好这是一个物理列,以便它存在于行中。它可以被查询并且不会基于 ORDER BY 或 WHERE 子句而改变。
解决方案
你可以试试这个:
--创建一个测试数据库(最后会被丢弃!小心真实数据!!)
USE master;
GO
CREATE DATABASE GroupingTest;
GO
USE GroupingTest;
GO
--您的表,我为您的 Id 列使用 IDENTITY 列
CREATE TABLE dbo.tbl(Id INT IDENTITY,GroupId INT,Whatever INT);
GO
--插入你的测试值
INSERT INTO tbl(GroupId, Whatever)
VALUES
(1,10)
,(1,10)
,(1,10)
,(2,10)
,(2,10)
,(3,10);
GO
--这是添加新列并最初填充它所必需的
ALTER TABLE tbl ADD GroupRowId INT;
GO
WITH cte AS
(
SELECT GroupRowId
,ROW_NUMBER() OVER(PARTITION BY GroupId ORDER BY Id) AS NewValue
FROM tbl
)
UPDATE cte SET GroupRowId=NewValue;
--check the result
SELECT * FROM tbl ORDER BY GroupId,Id;
GO
--现在我们创建一个触发器,它对新行的
作用完全相同 --非常重要:这必须适用于单个插入和多个插入!
CREATE TRIGGER dbo.SetNextGroupRowId ON dbo.tbl
FOR INSERT
AS
BEGIN
WITH cte AS
(
SELECT GroupRowId
,ROW_NUMBER() OVER(PARTITION BY GroupId ORDER BY Id) AS NewValue
FROM tbl
)
UPDATE cte
SET GroupRowId=NewValue
WHERE GroupRowId IS NULL; --<-- this ensures to change only new rows
END
GO
--现在我们可以用一个值来测试它
INSERT INTO tbl(GroupId, Whatever)
VALUES(1,20);
SELECT * FROM tbl ORDER BY GroupId,Id;
--我们可以用多个插入来测试它
INSERT INTO tbl(GroupId, Whatever)
VALUES
(1,30)
,(2,30)
,(2,30)
,(3,30)
,(4,30); --<-- the "4" is a new group
SELECT * FROM tbl ORDER BY GroupId,Id;
GO
- 打扫
USE master;
GO
DROP DATABASE GroupingTest;
你应该记住:
- 这可能会给手动插入的值
GroupRowId
或任何其他语句对该列的任何操作带来麻烦。 - 这可能会在删除行时遇到麻烦
- 您可以考虑
MAX(GroupRowId)+1
为给定组选择一种方法。这取决于您的需求。 - 您可以添加一个
unique index
onGroupId,GroupRowId
。这将 - 至少 - 避免两次给出相同的数字,但会导致错误。
...但是在您完美的业务规则世界中:-)这不会发生...
老实说:整个问题都有点味道……
推荐阅读
- java - 从命令行运行程序时找不到我的 java .class 文件(类不属于任何包)
- javascript - Vue Js 我应该为此使用 Vuex 吗?
- javascript - 如何从事件路径返回特定 HTML 节点的属性
- c# - dateTime 出错:不支持给定路径的格式
- java - 测试骆驼路线
- python - ValueError:导入sklearn时源代码字符串不能包含空字节
- python - GluonCV - 在目标检测中使用 GPU 进行推理
- ios - iOS 经常得到“10.00 秒用尽的真实(挂钟)时间余量”
- javascript - 单击菜单项时需要帮助关闭侧边栏/拉导航菜单
- machine-learning - 保存/重用基于 doc2vec 的模型以进行进一步预测