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

时间:2017-12-15 17:15:07

标签: sql sql-server triggers

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

| 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组中的最大数量。
没有必要重新分配其他值。我应该如何得到我需要的东西?

3 个答案:

答案 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