我有一个存储一些数据的表。每个数据行都有按组织和年份分组的序列号。下表的结构
| ID | Seq_num | Organisation | Year |
|----|---------|--------------|------|
| 1 | 1 | A | 2017 |
| 2 | 2 | A | 2017 |
| 3 | 3 | A | 2017 |
| 4 | 1 | A | 2018 |
| 5 | 2 | A | 2018 |
| 6 | 3 | A | 2018 |
| 7 | 1 | A | 2019 |
| 8 | 2 | A | 2019 |
| 9 | 4 | A | 2017 |
这个表有2个案例:
1)为新行分配序列号
2)修改列年份时重新分配序列号
对于第一种情况,我只需从组织和年份的Seq_num中选择max,如下所示:
UPDATE table
SET Seq_num =isnull(CurrMaxNum+1,1)
FROM table t
LEFT JOIN (SELECT DISTINCT Year, Organisation,
MAX(Seq_num) OVER (PARTITION BY Year, Organisation) AS CurrMaxNum
FROM table
GROUP BY Year,Organisation, Seq_num) as num
on num.Organisation=t.Organisation and num.Year=t.Year
WHERE (t.Seq_num IS Null OR t.Seq_num = 0 )
and (t.Organisation=num.Organisation and num.Year=t.Year )
对于第二种情况,如果年份被修改,我需要重新分配Seq_num。例如,如果我将id=5
的年份从2018年更改为2017年,则Seq_num
应从2变为4,因为4等于2017年的+1组中的最大数量。
没有必要重新分配其他值。我应该如何得到我需要的东西?
答案 0 :(得分:2)
我的解决方案。更新后触发器中的两个更新语句。很慢,但它的工作原理:
--for 2d case
UPDATE table
SET [Seq_num]= NULL
FROM table Z
INNER JOIN Inserted I ON Z.Id = I.Id
INNER JOIN Deleted D ON Z.Id = D.Id
WHERE
D.Year <> I.Year
--for 1st case
UPDATE table
SET [Seq_num]= s_num
FROM table Z
LEFT JOIN (select t.* , isnull(MAX([Seq_num]) over (partition by Year,Organisation
order by [Seq_num] desc)+1,1) as s_num
from table t
) S on Z.ID=S.ID
WHERE
Z.[Seq_num] IS NULL
因此,如果Year
发生变化,当前Seq_num
变为NULL
,则第二个UPDATE语句会为Seq_num
答案 1 :(得分:1)
评论太长了。
为什么要费心存储序列号?您可以使用以下方式动态生成它:
select t.*,
row_number() over (partition by organization, year order by id) as seq_num
from t;
您可能希望将数据存储在表中 - 例如,如果表格非常大并且性能存在问题。或者,如果您可能正在删除行并希望保留原始序列号。
但是,对于包含数千行的表格(以及#34;年份和#34;以及&#34;&#34;&#34;以及#34;使我认为表格不是那么大)的列表,即时计算是非常可行和简单的。
答案 2 :(得分:1)
我讨厌触发器,然而,一个用例似乎总是突然出现。 (我仍然会看到这是否可以在你的桌子update stored procedure
中控制。)
如果你想要一个触发器解决方案,那么我将创建一个仅UPDATE触发器并迭代所有插入的并更新修改年份值的记录。作为一个副作用,您创建了一个不透明的业务规则,如果有人插入触发器,您可能会遇到一个触发调用触发器的错误。
CREATE TRIGGER [dbo].[trMyTableModified]
ON [dbo].[MyTable]
AFTER UPDATE
AS BEGIN
SET NOCOUNT ON;
DECLARE @ID INT, @CurrentValueYear INT, @PriorValueYear INT, @OrganizationID INT
DECLARE X CURSOR FORWARD_ONLY READ_ONLY LOCAL FOR
SELECT ID,Year,OrganizationID FROM INSERTED
OPEN X
FETCH NEXT FROM X INTO @ID,@CurrentValueYear,@OrganizationID
WHILE @@FETCH_STATUS = 0 BEGIN
SELECT @PriorValueYear = Year FROM DELETED WHERE ID=@ID
IF(@PriorValueYear<>@CurrentValueYear) BEGIN
UPDATE MyTable
SET
MyTable.Seq_Num =
(
SELECT MAX(Seq_Num)
FROM MyTable T
WHERE
T.Organization=@OrganizationID
AND
T.Year=@CurrentValueYear
) + 1
WHERE
MyTable.ID= @ID
END
END
CLOSE X
DEALLOCATE X
END
进行检查和更新的一个更新查询。
但未经过测试,您可以检查更改,拉出最大序列并在一个查询中执行更新。它可能证明更有效。 INSERTED AND DELETED应始终包含相同数量的记录,即使由merge语句触发,因为合并将导致为每个操作触发多个触发器,更新,删除...
WITH YearChanges AS
(
SELECT I.ID,I.Year,I.OrganizationID
FROM
INSERTED I
INNER JOIN DELETED D ON D.ID=I.ID AND D.Year<>I.Year -- Ignore updates where year has not changed
),
WITH MaxSeq AS
(
SELECT Year,OrganizationID,NextSeqenceNumber = MAX(Seq_Num ) + 1
FROM
MyTable T
INNER JOIN YearChanges YC ON YC.OrganizationID = T.OrganizationID AND YC.Year=T.Year
GROUP BY
Year, OrganizationID
)
UPDATE
MyTable T
SET
Seq_Num = YC.NextSeqenceNumber
FROM
YearChanges YC
INNER JOIN MaxSeq MS ON MS.Year=YC.Year AND MS.OrganizationID=YC.OrganizationID
WHERE
T.ID=YC.ID