Триггер (ы) SQL Server для поддержки одной строки IsPrimary / IsDefault для каждого FK - PullRequest
0 голосов
/ 03 июля 2018

У нас есть несколько таблиц со столбцом IsPrimary, например, много членов, принадлежащих к учетной записи. Требование заключается в том, что если в учетной записи есть один или несколько участников, один и только один из них должен иметь IsPrimary = 1. Мы хотим добиться этого, используя триггеры как для максимальной гарантии целостности данных, так и для простоты использования в приложениях. Но из-за пакетной природы триггеров, я изо всех сил пытаюсь выполнить это и наиболее эффективным способом.

Пока у меня есть триггер insert / delete (см. Ниже), который обрабатывает вставку новой первичной записи или удаление первичной записи. Я застрял в том, чтобы убедиться, что первая вставленная запись имеет IsPrimary=1, а затем понял, что в пакете может быть несколько модификаций одной и той же учетной записи ...

У кого-нибудь есть опыт или пример с чем-то вроде этого?

ALTER TRIGGER dbo.trg_PrimaryTest_InsertDelete 
   ON  dbo.PrimaryTest 
   AFTER INSERT,DELETE
AS 
BEGIN
    -- SET NOCOUNT ON added to prevent extra result sets from
    -- interfering with SELECT statements.
    --SET NOCOUNT ON;
    PRINT 'executing trigger'

        --If inserting a primary, set all others to 0
        UPDATE      PrimaryTest 
        SET         IsPrimary = 0
        FROM        inserted
        INNER JOIN  PrimaryTest ON inserted.fk_ID = PrimaryTest.fk_ID
        WHERE       inserted.IsPrimary = 1 
                    AND PrimaryTest.pk_ID <> inserted.pk_ID
                    AND PrimaryTest.IsPrimary = 1

        --If deleting the primary, set most recent remaining phone to 1
        UPDATE      PrimaryTest
        SET         IsPrimary = 1
        WHERE       PrimaryTest.pk_ID IN (
                        SELECT TOP 1 PrimaryTest.pk_ID
                        FROM        deleted
                        INNER JOIN  PrimaryTest ON deleted.fk_ID = PrimaryTest.fk_ID
                        WHERE       deleted.IsPrimary = 1
                        ORDER BY    PrimaryTest.CreatedDate DESC
                    )

        PRINT 'trigger executed'
END
GO

Таблица ddl:

CREATE TABLE [dbo].[PrimaryTest](
    [pk_ID] [uniqueidentifier] NOT NULL,
    [Value] [nvarchar](50) NULL,
    [CreatedDate] [datetime2](7) NOT NULL,
    [IsPrimary] [bit] NOT NULL,
    [fk_ID] [int] NOT NULL,
 CONSTRAINT [PK_PrimaryTest] PRIMARY KEY CLUSTERED 
(
    [pk_ID] ASC
)
GO

EDIT:

Я думаю, что это может сработать или, по крайней мере, движется в правильном направлении. Я думаю, что он может читать больше записей, чем нужно во 2-й C.T.E. хотя дело. (Обратите внимание, я добавил update)

ALTER TRIGGER dbo.trg_PrimaryTest_InsertDelete 
   ON  dbo.PrimaryTest 
   AFTER INSERT, UPDATE, DELETE
AS 
BEGIN
    -- SET NOCOUNT ON added to prevent extra result sets from
    -- interfering with SELECT statements.
    --SET NOCOUNT ON;
    PRINT 'executing trigger'

        --If setting a new primary, set all others to 0
        UPDATE      PrimaryTest 
        SET         IsPrimary = 0
        FROM        inserted
        INNER JOIN  PrimaryTest ON inserted.fk_ID = PrimaryTest.fk_ID
        WHERE       inserted.IsPrimary = 1 
                    AND PrimaryTest.pk_ID <> inserted.pk_ID
                    AND PrimaryTest.IsPrimary = 1

        --Set IsPrimary on any modified sets left without an primary 
        ;WITH cte
        AS
        (
            SELECT  *, ROW_NUMBER() OVER(PARTITION BY fk_ID ORDER BY CreatedDate DESC) as RowNum 
            FROM    PrimaryTest p1
            WHERE   fk_ID IN (SELECT FK_ID FROM inserted UNION SELECT FK_ID FROM deleted)   --Only look at modified sets
                    AND NOT EXISTS (SELECT NULL FROM PrimaryTest p2 WHERE p2.fk_ID = p1.fk_ID AND p2.IsPrimary = 1) --Select rows in a set without an IsPrimary=1 record
        )
        UPDATE cte
        SET IsPrimary = 1
        WHERE RowNum = 1

    PRINT 'trigger executed'
END
GO
...