通过触发器修改列时重新分配序列号

问题描述 投票:0回答:3

我有一个存储一些数据的表。每个数据行都有按组织和年份分组的序列号。下表的结构

| 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)修改列Yea​​r时重新分配序列号 对于第一种情况,我只需从组织和年份的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组的最大数量。 无需重新分配其他值。我应该如何得到我需要的东西?

sql sql-server triggers
3个回答
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
投票

这个评论太长了。

为什么要打扰存储序列号?您可以使用以下方式动态生成:

select t.*,
       row_number() over (partition by organization, year order by id) as seq_num
from t;

您可能希望将数据存储在表中是有原因的 - 例如,如果表非常大并且性能存在问题。或者,如果您可能正在删除行并希望保留原始序列号。

但是,对于具有数千行的表(以及诸如“年”和“组织”之类的列导致我认为该表不那么大),动态计算是非常可行且更简单的。


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
© www.soinside.com 2019 - 2024. All rights reserved.