У нас есть несколько таблиц со столбцом 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