将 INSTEAD OF 触发器与 SQL 服务器时态表相结合以实现优势的解决方法
Workaround to achieve the benefits combining INSTEAD OF triggers with SQL Server Temporal tables
我想获得将 INSTEAD OF INSERT 和 INSTEAD OF UPDATE 触发器与 Temporal tables 相结合的好处。
INSTEAD OF 触发器在多个列中强制执行唯一性,其中一列在不同但相关的 table 中。如果用户尝试插入或更新打破唯一性不变量的记录,它们会抛出异常。
时间跟踪记录了什么、什么时候和(可能)谁。
因此,INSTEAD OF 触发器和时间跟踪都有好处。我很好奇是否有解决方法来实现两者的好处。
Temporal Table Considerations and Limitations 文档页面表明
INSTEAD OF triggers are not permitted on either the current or the history table to avoid invalidating the DML logic.
但也许有一个聪明的解决方法。
在下面的代码示例中,我想将时间跟踪添加到 [HubAssembly].[AftermarketParts] table。但它的 INSTEAD OF 触发器阻止将其变成临时 table.
CREATE SCHEMA [Part] AUTHORIZATION [dbo];
GO
CREATE SCHEMA [HubAssembly] AUTHORIZATION [dbo];
GO
CREATE TABLE [Part].[Types] (
[Id] INT IDENTITY (1, 1) NOT NULL,
[Name] NVARCHAR (50) NOT NULL,
CONSTRAINT [PK_Part.Types] PRIMARY KEY CLUSTERED ([Id] ASC),
CONSTRAINT [Part.Types.Name_Unique] UNIQUE NONCLUSTERED ([Name] ASC)
);
GO
CREATE TABLE [Part].[Parts] (
[PartNumber] NVARCHAR (18) NOT NULL,
[PartTypeId] INT NOT NULL,
CONSTRAINT [PK_Part.Parts] PRIMARY KEY CLUSTERED ([PartNumber] ASC),
CONSTRAINT [FK_Part.Parts_Part.Types_PartTypeId] FOREIGN KEY ([PartTypeId]) REFERENCES [Part].[Types] ([Id])
);
GO
CREATE TABLE [HubAssembly].[AftermarketPartUsages] (
[Id] INT IDENTITY (0, 1) NOT NULL,
[Name] NVARCHAR (50) NOT NULL,
CONSTRAINT [PK_HubAssembly.AftermarketPartUsages] PRIMARY KEY CLUSTERED ([Id] ASC),
CONSTRAINT [IX_HubAssembly_AftermarketPartUsages_Name_Unique] UNIQUE NONCLUSTERED ([Name] ASC),
);
GO
CREATE FUNCTION [Part].[f_PartTypeId] (@PartNumber AS NVARCHAR(18))
RETURNS INT
AS
BEGIN
DECLARE @Return AS INT
SET @Return = (SELECT PartTypeId FROM Part.Parts WHERE PartNumber = @PartNumber)
RETURN @Return
END
GO
CREATE TABLE [HubAssembly].[AftermarketParts] (
[Id] INT IDENTITY (0, 1) NOT NULL,
[HubAssemblyNumber] NVARCHAR(18) NOT NULL,
[AftermarketPartNumber] NVARCHAR(18) NOT NULL,
[Ranking] INT CONSTRAINT [DF_HubAssembly_AftermarketParts_Ranking] DEFAULT ((1)) NOT NULL,
[UsageId] INT DEFAULT ((0)) NOT NULL,
CONSTRAINT [PK_HubAssembly.AftermarketParts] PRIMARY KEY NONCLUSTERED ([Id] ASC),
CONSTRAINT [FK_HubAssembly.AftermarketParts_HubAssembly.AftermarketPartUsages_UsageId] FOREIGN KEY ([UsageId]) REFERENCES [HubAssembly].[AftermarketPartUsages] ([Id]),
-- FK_HubAssembly.AftermarketParts_Part.Parts_HubAssemblyNumber is simplified here. It actually is a foreign key to HubAssembly.HubAssemblies.HubAssemblyNumber which is a FK to Part.Parts.PartNumber
CONSTRAINT [FK_HubAssembly.AftermarketParts_Part.Parts_HubAssemblyNumber] FOREIGN KEY ([HubAssemblyNumber]) REFERENCES [Part].[Parts] ([PartNumber]),
CONSTRAINT [FK_HubAssembly.AftermarketParts_Part.Parts_AftermarketPartNumber] FOREIGN KEY ([AftermarketPartNumber]) REFERENCES [Part].[Parts] ([PartNumber]),
);
GO
CREATE TRIGGER [HubAssembly].[Trigger_AftermarketParts_InsteadOf_Update] ON [HubAssembly].[AftermarketParts]
INSTEAD OF UPDATE
AS
BEGIN
SET NOCOUNT ON;
DECLARE @AnyDuplicates AS BIT = 0;
WITH cte AS (
SELECT HubAssemblyNumber, Ranking, Part.f_PartTypeId(AftermarketPartNumber) AS PartTypeId, AftermarketPartNumber, UsageId
FROM INSERTED
UNION
SELECT HubAssemblyNumber, Ranking, Part.f_PartTypeId(AftermarketPartNumber), AftermarketPartNumber, UsageId
FROM [HubAssembly].[AftermarketParts]
WHERE HubAssemblyNumber IN (SELECT HubAssemblyNumber FROM INSERTED)
AND Id NOT IN (SELECT Id FROM DELETED)
)
SELECT TOP 1 @AnyDuplicates = 1
FROM cte
GROUP BY HubAssemblyNumber, PartTypeId, Ranking, UsageId
HAVING (COUNT(AftermarketPartNumber) > 1)
IF @AnyDuplicates = 1
THROW 50001, N'Performing this update would result in duplicate values for ([HubAssemblyNumber], [Ranking], [Part].[f_PartTypeId](AftermarketPartNumber), [UsageId]). This tuple should have distinct values for all rows in the table.', 1;
ELSE
BEGIN
DELETE FROM [HubAssembly].[AftermarketParts] WHERE Id IN (SELECT Id FROM DELETED);
INSERT INTO [HubAssembly].[AftermarketParts] ([HubAssemblyNumber], [AftermarketPartNumber], [Ranking], [UsageId])
SELECT [HubAssemblyNumber], [AftermarketPartNumber], [Ranking], [UsageId]
FROM INSERTED;
END;
END
GO
CREATE TRIGGER [HubAssembly].[Trigger_AftermarketParts_InsteadOf_Insert] ON [HubAssembly].[AftermarketParts]
INSTEAD OF INSERT
AS
BEGIN
SET NOCOUNT ON;
DECLARE @AnyDuplicates AS BIT = 0;
WITH cte AS (
SELECT HubAssemblyNumber, Ranking, Part.f_PartTypeId(AftermarketPartNumber) AS PartTypeId, AftermarketPartNumber, UsageId
FROM INSERTED
UNION
SELECT HubAssemblyNumber, Ranking, Part.f_PartTypeId(AftermarketPartNumber), AftermarketPartNumber, UsageId
FROM [HubAssembly].[AftermarketParts]
WHERE HubAssemblyNumber IN (SELECT HubAssemblyNumber FROM INSERTED)
)
SELECT TOP 1 @AnyDuplicates = 1
FROM cte
GROUP BY HubAssemblyNumber, PartTypeId, Ranking, UsageId
HAVING (COUNT(AftermarketPartNumber) > 1)
IF @AnyDuplicates = 1
THROW 50001, N'Performing this insert would result in duplicate values for ([HubAssemblyNumber], [Ranking], [Part].[f_PartTypeId](AftermarketPartNumber), [UsageId]). This tuple should have distinct values for all rows in the table.', 1;
ELSE
INSERT INTO [HubAssembly].[AftermarketParts] ([HubAssemblyNumber], [AftermarketPartNumber], [Ranking], [UsageId])
SELECT [HubAssemblyNumber], [AftermarketPartNumber], [Ranking], [UsageId]
FROM INSERTED;
END
GO
CREATE UNIQUE CLUSTERED INDEX [HubAssemblyNumber_AftermarketPartNumber_UsageId_Unique]
ON [HubAssembly].[AftermarketParts]([HubAssemblyNumber] ASC, [AftermarketPartNumber] ASC, [UsageId] ASC);
GO
这里有一些数据来填充这些 table:
SET IDENTITY_INSERT Part.Types ON;
GO
INSERT INTO Part.Types (Id, Name) VALUES (5, N'Wheel Stud');
INSERT INTO Part.Types (Id, Name) VALUES (7, N'ABS Tone Ring');
INSERT INTO Part.Types (Id, Name) VALUES (8, N'Port Plug');
INSERT INTO Part.Types (Id, Name) VALUES (14, N'Seal');
INSERT INTO Part.Types (Id, Name) VALUES (217, N'PreSet Complete Hub Rebuild Kit (Keyway)');
INSERT INTO Part.Types (Id, Name) VALUES (221, N'Wheel Seal and Spacer Kit');
INSERT INTO Part.Types (Id, Name) VALUES (115, N'Hub Assembly');
INSERT INTO Part.Types (Id, Name) VALUES (6, N'Double-Ended Stud');
INSERT INTO Part.Types (Id, Name) VALUES (101, N'Bearing Cup & Cone Assembly');
GO
SET IDENTITY_INSERT Part.Types OFF;
GO
INSERT INTO Part.Parts (PartNumber, PartTypeId) VALUES (N'103705', 7);
INSERT INTO Part.Parts (PartNumber, PartTypeId) VALUES (N'105272', 8);
INSERT INTO Part.Parts (PartNumber, PartTypeId) VALUES (N'10001405', 5);
INSERT INTO Part.Parts (PartNumber, PartTypeId) VALUES (N'10009780', 7);
INSERT INTO Part.Parts (PartNumber, PartTypeId) VALUES (N'10018436', 5);
INSERT INTO Part.Parts (PartNumber, PartTypeId) VALUES (N'10045871', 217);
INSERT INTO Part.Parts (PartNumber, PartTypeId) VALUES (N'10080124', 14);
INSERT INTO Part.Parts (PartNumber, PartTypeId) VALUES (N'10081518', 221);
INSERT INTO Part.Parts (PartNumber, PartTypeId) VALUES (N'10082204', 115);
INSERT INTO Part.Parts (PartNumber, PartTypeId) VALUES (N'10083796', 115);
INSERT INTO Part.Parts (PartNumber, PartTypeId) VALUES (N'10018442', 6);
INSERT INTO Part.Parts (PartNumber, PartTypeId) VALUES (N'10041905', 101);
INSERT INTO Part.Parts (PartNumber, PartTypeId) VALUES (N'10041906', 101);
INSERT INTO Part.Parts (PartNumber, PartTypeId) VALUES (N'10041915', 101);
INSERT INTO Part.Parts (PartNumber, PartTypeId) VALUES (N'10041916', 101);
GO
SET IDENTITY_INSERT HubAssembly.AftermarketPartUsages ON;
INSERT INTO HubAssembly.AftermarketPartUsages(Id, Name) VALUES (0, N'—');
INSERT INTO HubAssembly.AftermarketPartUsages(Id, Name) VALUES (1, N'Inboard Bearing Set');
INSERT INTO HubAssembly.AftermarketPartUsages(Id, Name) VALUES (2, N'Outboard Bearing Set');
INSERT INTO HubAssembly.AftermarketPartUsages(Id, Name) VALUES (3, N'Axle Stud');
GO
SET IDENTITY_INSERT HubAssembly.AftermarketPartUsages OFF;
GO
INSERT INTO HubAssembly.AftermarketParts (HubAssemblyNumber, AftermarketPartNumber, Ranking, UsageId) VALUES (N'10082204', N'10001405', 1, 0);
INSERT INTO HubAssembly.AftermarketParts (HubAssemblyNumber, AftermarketPartNumber, Ranking, UsageId) VALUES (N'10082204', N'10009780', 1, 0);
INSERT INTO HubAssembly.AftermarketParts (HubAssemblyNumber, AftermarketPartNumber, Ranking, UsageId) VALUES (N'10082204', N'10041905', 1, 1);
INSERT INTO HubAssembly.AftermarketParts (HubAssemblyNumber, AftermarketPartNumber, Ranking, UsageId) VALUES (N'10082204', N'10041906', 1, 2);
INSERT INTO HubAssembly.AftermarketParts (HubAssemblyNumber, AftermarketPartNumber, Ranking, UsageId) VALUES (N'10082204', N'10045871', 1, 0);
INSERT INTO HubAssembly.AftermarketParts (HubAssemblyNumber, AftermarketPartNumber, Ranking, UsageId) VALUES (N'10082204', N'10081518', 1, 0);
INSERT INTO HubAssembly.AftermarketParts (HubAssemblyNumber, AftermarketPartNumber, Ranking, UsageId) VALUES (N'10083796', N'10018436', 1, 0);
INSERT INTO HubAssembly.AftermarketParts (HubAssemblyNumber, AftermarketPartNumber, Ranking, UsageId) VALUES (N'10083796', N'10018442', 1, 3);
INSERT INTO HubAssembly.AftermarketParts (HubAssemblyNumber, AftermarketPartNumber, Ranking, UsageId) VALUES (N'10083796', N'10041915', 1, 1);
INSERT INTO HubAssembly.AftermarketParts (HubAssemblyNumber, AftermarketPartNumber, Ranking, UsageId) VALUES (N'10083796', N'10041916', 1, 2);
INSERT INTO HubAssembly.AftermarketParts (HubAssemblyNumber, AftermarketPartNumber, Ranking, UsageId) VALUES (N'10083796', N'10080124', 1, 0);
INSERT INTO HubAssembly.AftermarketParts (HubAssemblyNumber, AftermarketPartNumber, Ranking, UsageId) VALUES (N'10083796', N'103705', 1, 0);
INSERT INTO HubAssembly.AftermarketParts (HubAssemblyNumber, AftermarketPartNumber, Ranking, UsageId) VALUES (N'10083796', N'105272', 1, 0);
GO
INSTEAD OF 触发器强制跨 [HubAssembly] 中的四列(HubAssemblyNumber、Ranking、AftermarketPartNumber、UsageId)的唯一性。[AftermarketParts] table 和相关 [Part] 中的一列。[Parts] table 通过 Part.f_PartTypeId() 函数。
在插入和更新时在数据库中强制执行 two-table/five-column 唯一性验证是理想的。不幸的是,执行强制执行的触发器禁止将 table 转换为时间 table。但由于自动更改跟踪也非常有用,我想在 table 中添加时间跟踪。
还有其他方法可以实现这些双重好处吗?
答案很简单。只需将 INSTEAD OF
触发器替换为 AFTER
触发器就可以了。
我想获得将 INSTEAD OF INSERT 和 INSTEAD OF UPDATE 触发器与 Temporal tables 相结合的好处。
INSTEAD OF 触发器在多个列中强制执行唯一性,其中一列在不同但相关的 table 中。如果用户尝试插入或更新打破唯一性不变量的记录,它们会抛出异常。
时间跟踪记录了什么、什么时候和(可能)谁。
因此,INSTEAD OF 触发器和时间跟踪都有好处。我很好奇是否有解决方法来实现两者的好处。
Temporal Table Considerations and Limitations 文档页面表明
INSTEAD OF triggers are not permitted on either the current or the history table to avoid invalidating the DML logic.
但也许有一个聪明的解决方法。
在下面的代码示例中,我想将时间跟踪添加到 [HubAssembly].[AftermarketParts] table。但它的 INSTEAD OF 触发器阻止将其变成临时 table.
CREATE SCHEMA [Part] AUTHORIZATION [dbo];
GO
CREATE SCHEMA [HubAssembly] AUTHORIZATION [dbo];
GO
CREATE TABLE [Part].[Types] (
[Id] INT IDENTITY (1, 1) NOT NULL,
[Name] NVARCHAR (50) NOT NULL,
CONSTRAINT [PK_Part.Types] PRIMARY KEY CLUSTERED ([Id] ASC),
CONSTRAINT [Part.Types.Name_Unique] UNIQUE NONCLUSTERED ([Name] ASC)
);
GO
CREATE TABLE [Part].[Parts] (
[PartNumber] NVARCHAR (18) NOT NULL,
[PartTypeId] INT NOT NULL,
CONSTRAINT [PK_Part.Parts] PRIMARY KEY CLUSTERED ([PartNumber] ASC),
CONSTRAINT [FK_Part.Parts_Part.Types_PartTypeId] FOREIGN KEY ([PartTypeId]) REFERENCES [Part].[Types] ([Id])
);
GO
CREATE TABLE [HubAssembly].[AftermarketPartUsages] (
[Id] INT IDENTITY (0, 1) NOT NULL,
[Name] NVARCHAR (50) NOT NULL,
CONSTRAINT [PK_HubAssembly.AftermarketPartUsages] PRIMARY KEY CLUSTERED ([Id] ASC),
CONSTRAINT [IX_HubAssembly_AftermarketPartUsages_Name_Unique] UNIQUE NONCLUSTERED ([Name] ASC),
);
GO
CREATE FUNCTION [Part].[f_PartTypeId] (@PartNumber AS NVARCHAR(18))
RETURNS INT
AS
BEGIN
DECLARE @Return AS INT
SET @Return = (SELECT PartTypeId FROM Part.Parts WHERE PartNumber = @PartNumber)
RETURN @Return
END
GO
CREATE TABLE [HubAssembly].[AftermarketParts] (
[Id] INT IDENTITY (0, 1) NOT NULL,
[HubAssemblyNumber] NVARCHAR(18) NOT NULL,
[AftermarketPartNumber] NVARCHAR(18) NOT NULL,
[Ranking] INT CONSTRAINT [DF_HubAssembly_AftermarketParts_Ranking] DEFAULT ((1)) NOT NULL,
[UsageId] INT DEFAULT ((0)) NOT NULL,
CONSTRAINT [PK_HubAssembly.AftermarketParts] PRIMARY KEY NONCLUSTERED ([Id] ASC),
CONSTRAINT [FK_HubAssembly.AftermarketParts_HubAssembly.AftermarketPartUsages_UsageId] FOREIGN KEY ([UsageId]) REFERENCES [HubAssembly].[AftermarketPartUsages] ([Id]),
-- FK_HubAssembly.AftermarketParts_Part.Parts_HubAssemblyNumber is simplified here. It actually is a foreign key to HubAssembly.HubAssemblies.HubAssemblyNumber which is a FK to Part.Parts.PartNumber
CONSTRAINT [FK_HubAssembly.AftermarketParts_Part.Parts_HubAssemblyNumber] FOREIGN KEY ([HubAssemblyNumber]) REFERENCES [Part].[Parts] ([PartNumber]),
CONSTRAINT [FK_HubAssembly.AftermarketParts_Part.Parts_AftermarketPartNumber] FOREIGN KEY ([AftermarketPartNumber]) REFERENCES [Part].[Parts] ([PartNumber]),
);
GO
CREATE TRIGGER [HubAssembly].[Trigger_AftermarketParts_InsteadOf_Update] ON [HubAssembly].[AftermarketParts]
INSTEAD OF UPDATE
AS
BEGIN
SET NOCOUNT ON;
DECLARE @AnyDuplicates AS BIT = 0;
WITH cte AS (
SELECT HubAssemblyNumber, Ranking, Part.f_PartTypeId(AftermarketPartNumber) AS PartTypeId, AftermarketPartNumber, UsageId
FROM INSERTED
UNION
SELECT HubAssemblyNumber, Ranking, Part.f_PartTypeId(AftermarketPartNumber), AftermarketPartNumber, UsageId
FROM [HubAssembly].[AftermarketParts]
WHERE HubAssemblyNumber IN (SELECT HubAssemblyNumber FROM INSERTED)
AND Id NOT IN (SELECT Id FROM DELETED)
)
SELECT TOP 1 @AnyDuplicates = 1
FROM cte
GROUP BY HubAssemblyNumber, PartTypeId, Ranking, UsageId
HAVING (COUNT(AftermarketPartNumber) > 1)
IF @AnyDuplicates = 1
THROW 50001, N'Performing this update would result in duplicate values for ([HubAssemblyNumber], [Ranking], [Part].[f_PartTypeId](AftermarketPartNumber), [UsageId]). This tuple should have distinct values for all rows in the table.', 1;
ELSE
BEGIN
DELETE FROM [HubAssembly].[AftermarketParts] WHERE Id IN (SELECT Id FROM DELETED);
INSERT INTO [HubAssembly].[AftermarketParts] ([HubAssemblyNumber], [AftermarketPartNumber], [Ranking], [UsageId])
SELECT [HubAssemblyNumber], [AftermarketPartNumber], [Ranking], [UsageId]
FROM INSERTED;
END;
END
GO
CREATE TRIGGER [HubAssembly].[Trigger_AftermarketParts_InsteadOf_Insert] ON [HubAssembly].[AftermarketParts]
INSTEAD OF INSERT
AS
BEGIN
SET NOCOUNT ON;
DECLARE @AnyDuplicates AS BIT = 0;
WITH cte AS (
SELECT HubAssemblyNumber, Ranking, Part.f_PartTypeId(AftermarketPartNumber) AS PartTypeId, AftermarketPartNumber, UsageId
FROM INSERTED
UNION
SELECT HubAssemblyNumber, Ranking, Part.f_PartTypeId(AftermarketPartNumber), AftermarketPartNumber, UsageId
FROM [HubAssembly].[AftermarketParts]
WHERE HubAssemblyNumber IN (SELECT HubAssemblyNumber FROM INSERTED)
)
SELECT TOP 1 @AnyDuplicates = 1
FROM cte
GROUP BY HubAssemblyNumber, PartTypeId, Ranking, UsageId
HAVING (COUNT(AftermarketPartNumber) > 1)
IF @AnyDuplicates = 1
THROW 50001, N'Performing this insert would result in duplicate values for ([HubAssemblyNumber], [Ranking], [Part].[f_PartTypeId](AftermarketPartNumber), [UsageId]). This tuple should have distinct values for all rows in the table.', 1;
ELSE
INSERT INTO [HubAssembly].[AftermarketParts] ([HubAssemblyNumber], [AftermarketPartNumber], [Ranking], [UsageId])
SELECT [HubAssemblyNumber], [AftermarketPartNumber], [Ranking], [UsageId]
FROM INSERTED;
END
GO
CREATE UNIQUE CLUSTERED INDEX [HubAssemblyNumber_AftermarketPartNumber_UsageId_Unique]
ON [HubAssembly].[AftermarketParts]([HubAssemblyNumber] ASC, [AftermarketPartNumber] ASC, [UsageId] ASC);
GO
这里有一些数据来填充这些 table:
SET IDENTITY_INSERT Part.Types ON;
GO
INSERT INTO Part.Types (Id, Name) VALUES (5, N'Wheel Stud');
INSERT INTO Part.Types (Id, Name) VALUES (7, N'ABS Tone Ring');
INSERT INTO Part.Types (Id, Name) VALUES (8, N'Port Plug');
INSERT INTO Part.Types (Id, Name) VALUES (14, N'Seal');
INSERT INTO Part.Types (Id, Name) VALUES (217, N'PreSet Complete Hub Rebuild Kit (Keyway)');
INSERT INTO Part.Types (Id, Name) VALUES (221, N'Wheel Seal and Spacer Kit');
INSERT INTO Part.Types (Id, Name) VALUES (115, N'Hub Assembly');
INSERT INTO Part.Types (Id, Name) VALUES (6, N'Double-Ended Stud');
INSERT INTO Part.Types (Id, Name) VALUES (101, N'Bearing Cup & Cone Assembly');
GO
SET IDENTITY_INSERT Part.Types OFF;
GO
INSERT INTO Part.Parts (PartNumber, PartTypeId) VALUES (N'103705', 7);
INSERT INTO Part.Parts (PartNumber, PartTypeId) VALUES (N'105272', 8);
INSERT INTO Part.Parts (PartNumber, PartTypeId) VALUES (N'10001405', 5);
INSERT INTO Part.Parts (PartNumber, PartTypeId) VALUES (N'10009780', 7);
INSERT INTO Part.Parts (PartNumber, PartTypeId) VALUES (N'10018436', 5);
INSERT INTO Part.Parts (PartNumber, PartTypeId) VALUES (N'10045871', 217);
INSERT INTO Part.Parts (PartNumber, PartTypeId) VALUES (N'10080124', 14);
INSERT INTO Part.Parts (PartNumber, PartTypeId) VALUES (N'10081518', 221);
INSERT INTO Part.Parts (PartNumber, PartTypeId) VALUES (N'10082204', 115);
INSERT INTO Part.Parts (PartNumber, PartTypeId) VALUES (N'10083796', 115);
INSERT INTO Part.Parts (PartNumber, PartTypeId) VALUES (N'10018442', 6);
INSERT INTO Part.Parts (PartNumber, PartTypeId) VALUES (N'10041905', 101);
INSERT INTO Part.Parts (PartNumber, PartTypeId) VALUES (N'10041906', 101);
INSERT INTO Part.Parts (PartNumber, PartTypeId) VALUES (N'10041915', 101);
INSERT INTO Part.Parts (PartNumber, PartTypeId) VALUES (N'10041916', 101);
GO
SET IDENTITY_INSERT HubAssembly.AftermarketPartUsages ON;
INSERT INTO HubAssembly.AftermarketPartUsages(Id, Name) VALUES (0, N'—');
INSERT INTO HubAssembly.AftermarketPartUsages(Id, Name) VALUES (1, N'Inboard Bearing Set');
INSERT INTO HubAssembly.AftermarketPartUsages(Id, Name) VALUES (2, N'Outboard Bearing Set');
INSERT INTO HubAssembly.AftermarketPartUsages(Id, Name) VALUES (3, N'Axle Stud');
GO
SET IDENTITY_INSERT HubAssembly.AftermarketPartUsages OFF;
GO
INSERT INTO HubAssembly.AftermarketParts (HubAssemblyNumber, AftermarketPartNumber, Ranking, UsageId) VALUES (N'10082204', N'10001405', 1, 0);
INSERT INTO HubAssembly.AftermarketParts (HubAssemblyNumber, AftermarketPartNumber, Ranking, UsageId) VALUES (N'10082204', N'10009780', 1, 0);
INSERT INTO HubAssembly.AftermarketParts (HubAssemblyNumber, AftermarketPartNumber, Ranking, UsageId) VALUES (N'10082204', N'10041905', 1, 1);
INSERT INTO HubAssembly.AftermarketParts (HubAssemblyNumber, AftermarketPartNumber, Ranking, UsageId) VALUES (N'10082204', N'10041906', 1, 2);
INSERT INTO HubAssembly.AftermarketParts (HubAssemblyNumber, AftermarketPartNumber, Ranking, UsageId) VALUES (N'10082204', N'10045871', 1, 0);
INSERT INTO HubAssembly.AftermarketParts (HubAssemblyNumber, AftermarketPartNumber, Ranking, UsageId) VALUES (N'10082204', N'10081518', 1, 0);
INSERT INTO HubAssembly.AftermarketParts (HubAssemblyNumber, AftermarketPartNumber, Ranking, UsageId) VALUES (N'10083796', N'10018436', 1, 0);
INSERT INTO HubAssembly.AftermarketParts (HubAssemblyNumber, AftermarketPartNumber, Ranking, UsageId) VALUES (N'10083796', N'10018442', 1, 3);
INSERT INTO HubAssembly.AftermarketParts (HubAssemblyNumber, AftermarketPartNumber, Ranking, UsageId) VALUES (N'10083796', N'10041915', 1, 1);
INSERT INTO HubAssembly.AftermarketParts (HubAssemblyNumber, AftermarketPartNumber, Ranking, UsageId) VALUES (N'10083796', N'10041916', 1, 2);
INSERT INTO HubAssembly.AftermarketParts (HubAssemblyNumber, AftermarketPartNumber, Ranking, UsageId) VALUES (N'10083796', N'10080124', 1, 0);
INSERT INTO HubAssembly.AftermarketParts (HubAssemblyNumber, AftermarketPartNumber, Ranking, UsageId) VALUES (N'10083796', N'103705', 1, 0);
INSERT INTO HubAssembly.AftermarketParts (HubAssemblyNumber, AftermarketPartNumber, Ranking, UsageId) VALUES (N'10083796', N'105272', 1, 0);
GO
INSTEAD OF 触发器强制跨 [HubAssembly] 中的四列(HubAssemblyNumber、Ranking、AftermarketPartNumber、UsageId)的唯一性。[AftermarketParts] table 和相关 [Part] 中的一列。[Parts] table 通过 Part.f_PartTypeId() 函数。
在插入和更新时在数据库中强制执行 two-table/five-column 唯一性验证是理想的。不幸的是,执行强制执行的触发器禁止将 table 转换为时间 table。但由于自动更改跟踪也非常有用,我想在 table 中添加时间跟踪。
还有其他方法可以实现这些双重好处吗?
答案很简单。只需将 INSTEAD OF
触发器替换为 AFTER
触发器就可以了。