如何处理此Sql Server触发器中的多行?

时间:2010-02-04 03:25:11

标签: sql-server sql-server-2008 triggers

我有以下触发器,但由于trigger needs to handle multiple records,我不知道如何在我的触发器代码中正确处理它。

有人可以建议我如何更改下面的TSql以正确处理多条记录,而不只是一条记录(如下面列出的那样)。

表架构和默认值。

CREATE TABLE [dbo].[tblArticle](
 [IdArticle] [int] IDENTITY(1,1) NOT NULL,
 [IdArticleStatus] [tinyint] NOT NULL,
 [Title] [nvarchar](200) NOT NULL,
 [CleanTitle] [nvarchar](300) NOT NULL,
 [UniqueTitle] [nvarchar](300) NOT NULL,
 [Content] [nvarchar](max) NOT NULL
GO

ALTER TABLE [dbo].[tblArticle] ADD  CONSTRAINT [DF_tblArticle_CleanTitle]  
    DEFAULT (newid()) FOR [CleanTitle]
GO

ALTER TABLE [dbo].[tblArticle] ADD  CONSTRAINT [DF_tblArticle_UniqueTitle]
    DEFAULT (newid()) FOR [UniqueTitle]
GO

触发器,仅处理单个记录...不是多个。

ALTER TRIGGER [dbo].[ArticlesAfterInsertOrUpdate]
   ON  [dbo].[tblArticle] 
   AFTER INSERT,UPDATE
AS 
BEGIN
 SET NOCOUNT ON

    DECLARE @IdArticle INTEGER,
        @Title NVARCHAR(300),
        @CleanTitle NVARCHAR(300),
        @UniqueTitle NVARCHAR(300),
        @NewCleanTitle NVARCHAR(300),
        @CleanTitleCount INTEGER

    -- Only Update the CleanTitle and UniqueTitle if *required*
    -- This means, create a unique subject of the title, then check if this clean value
    -- is different to the current clean value. If so, then update both clean and unique.
    -- Otherwise, don't do anything (because it will include this row in the count check, below).
    IF UPDATE(Title) BEGIN

        -- TODO: How will this handle multiple records???
        SELECT @IdArticle = IdArticle, @Title = Title, @CleanTitle = CleanTitle
        FROM INSERTED

        -- Create the 'Slugs'.
        SET @NewCleanTitle = dbo.CreateUniqueSubject(@Title) 
        SET @UniqueTitle = @NewCleanTitle

        IF @NewCleanTitle != @CleanTitle BEGIN

            -- We need to update the clean and unique, so lets get started...

            -- Grab the count :: eg. how many other _clean_ titles already exist?
            -- Note: this is the _only_ reason why we have this
            -- column - because it has an index on it.
            SELECT @CleanTitleCount = COUNT(IdArticle)
            FROM [dbo].[tblArticle]
            WHERE CleanTitle = @NewCleanTitle

            -- If we have some previous titles, then we need to append a number
            -- to the end of the current slug.
            IF @CleanTitleCount > 0
                SET @UniqueTitle = @NewCleanTitle + CAST((@CleanTitleCount + 1) AS VARCHAR(10))

            -- Now update the unique subject field.
            UPDATE [dbo].[tblArticle]
            SET CleanTitle = @NewCleanTitle,
                UniqueTitle = @UniqueTitle
            WHERE IdArticle = @IdArticle
        END
    END
END
GO

请帮忙!

1 个答案:

答案 0 :(得分:1)

不需要知道自定义函数的作用,只是它为每个给定的输入(即标题)返回相同的值。在触发器中执行这种类型的逻辑会有点复杂,但你当然可以实现它。肯定还有其他方法可以让它工作,最好的方法完全取决于你的环境,但是下面的逻辑会让你得到你想要的东西作为起点:

ALTER TRIGGER [dbo].[ArticlesAfterInsertOrUpdate]
   ON  [dbo].[tblArticle] 
   AFTER INSERT,UPDATE
AS 
BEGIN
 SET NOCOUNT ON


    -- Only Update the CleanTitle and UniqueTitle if *required*
    -- This means, create a unique subject of the title, then check if this clean value
    -- is different to the current clean value. If so, then update both clean and unique.
    -- Otherwise, don't do anything (because it will include this row in the count check, below).
    IF UPDATE(Title) BEGIN
        -- Materialize with the newCleanTitle value for simplicity sake, could
        -- do this inline below, not sure which would work better in your environment
        if object_id('tempdb..#tempIData') > 0
            drop table #tempIData;
        select  *, 
                dbo.CreateUniqueSubject(i.Title) as newCleanTitle
        into    #tempIData
        from    inserted i
        where   i.CleanTitle <> dbo.CreateUniqueSubject(i.Title);

        with iData as
        (   -- Get the data inserted along with a running tally of any duplicate
            -- newCleanTitle values
            select  i.IdArticle as IdArticle, 
                    i.CleanTitle, i.newCleanTitle,
                    -- Need to get the count here as well to account for cases where
                    -- we insert multiple records with the same resulting cleanTitle
                    cast(row_number() over(partition by i.newCleanTitle order by i.IdArticle) as bigint) as cntCleanTitle
            from    #tempIData i
        ),
        srcData as
        (   -- Get the existing count of data by CleanTitle value for each
            -- newCleanTitle included in the inserted data
            select  t.CleanTitle as CleanTitle,
                    cast(coalesce(count(*),0) as bigint) as cntCleanTitle
            from    dbo.tblArticle t
            join    
                    (   -- Need a distinct list of newCleanTitle values
                        select  a.newCleanTitle
                        from    iData a
                        group by a.newCleanTitle
                    ) i
                    -- Join on CleanTitle as we need to get the existing running
                    -- count for each distinct CleanTitle values
            on      t.CleanTitle = i.newCleanTitle
            group by t.CleanTitle
        )
        -- Do the update...
        update  a
        set     a.CleanTitle = i.newCleanTitle,
                a.UniqueTitle =
                    case 
                        when i.cntCleanTitle + coalesce(s.cntCleanTitle,0) > 1 
                            then i.newCleanTitle + cast((cast(i.cntCleanTitle as bigint) + cast(coalesce(s.cntCleanTitle,0) as bigint)) as nvarchar(10)) 
                        else 
                            i.newCleanTitle 
                    end
        from    dbo.tblArticle a
        join    iData i
        on      a.IdArticle = i.IdArticle
        left join srcData s
        on      i.newCleanTitle = s.CleanTitle;

        if object_id('tempdb..#tempIData') > 0
            drop table #tempIData;

    END
END