通过触发器修改列时重新分配序列号
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了通过触发器修改列时重新分配序列号相关的知识,希望对你有一定的参考价值。
我有一个存储一些数据的表。每个数据行都有按组织和年份分组的序列号。下表的结构
| 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)修改列Year时重新分配序列号 对于第一种情况,我只需从组织和年份的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。例如,如果我从2018年到2017年改变id=5
的年份,Seq_num
应该从2变为4,因为4等于2017年的+1组的最大数量。
无需重新分配其他值。我应该如何得到我需要的东西?
我的解决方案更新后触发器中的两个更新语句。很慢,但它的工作原理:
--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
分配新值
这个评论太长了。
为什么要打扰存储序列号?您可以使用以下方式动态生成:
select t.*,
row_number() over (partition by organization, year order by id) as seq_num
from t;
您可能希望将数据存储在表中是有原因的 - 例如,如果表非常大并且性能存在问题。或者,如果您可能正在删除行并希望保留原始序列号。
但是,对于具有数千行的表(以及诸如“年”和“组织”之类的列导致我认为该表不那么大),动态计算是非常可行且更简单的。
我讨厌触发器,然而,一个用例似乎总是突然出现。 (我仍然会看到这是否可以在你的桌子的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
以上是关于通过触发器修改列时重新分配序列号的主要内容,如果未能解决你的问题,请参考以下文章
如何防止片段在活动重新创建时触发 onCreate onCreateView