我不知道的任何手段強制執行的共變通過標準唯一性約束或檢查約束或任何其他優雅的解決方案來處理這些問題。但是,您可以通過僅允許通過存儲過程訪問表或者定義具有「INSTEAD OF INSERT」觸發器的視圖來強制執行約束。
方法1 - 存儲過程
以下示例顯示了存儲過程的方法。首先我們創建一個表值類型,以便我們可以將我們的協變組作爲只讀參數傳遞給我們的存儲過程。
CREATE TYPE CovariateGroupEntry AS TABLE
(
[Rank] INT NOT NULL
,[Covariate] NVARCHAR(50)
PRIMARY KEY([Rank], [Covariate])
)
接下來我們創建基表將包含我們的協羣:
CREATE TABLE CovariateGroups
(
[ID] INT NOT NULL
,[Rank] INT NOT NULL
,[Covariate] NVARCHAR(50)
PRIMARY KEY([ID], [Rank], [Covariate])
)
下一步,我們創建一個將被用來自動生成我們的ID的虛表:
CREATE TABLE CovariateGroupIDs
(
[GroupID] INT PRIMARY KEY IDENTITY
,[CreatedDateTime] DATETIME NOT NULL
)
我們創建我們的程序的最後一步:
CREATE PROCEDURE CovariateGroup_Add
(
@covariateGroupEntry dbo.CovariateGroupEntry READONLY
)
AS
BEGIN
SET NOCOUNT ON;
DECLARE @groupID INT;
DECLARE @groupSize INT;
DECLARE @groupMatchCount INT;
DECLARE @minRank INT;
DECLARE @maxRankDelta INT;
DECLARE @minRankDelta INT;
-- Get the size of the new group which user will attempt to add.
SELECT @groupSize = COUNT([Rank])
FROM @covariateGroupEntry
-- Validate that the new group rank starts at 1 and increments by 1 step value only.
SELECT @minRank = ISNULL(MIN([Rank]), 0)
,@maxRankDelta = ISNULL(MAX(Delta), 0)
,@minRankDelta = ISNULL(MIN(Delta), 0)
FROM (
SELECT [Rank]
,[Rank] - (LAG([Rank], 1, 0) OVER (ORDER BY [Rank])) AS Delta
FROM @covariateGroupEntry
) RankValidation
IF ((@minRank > 1) OR (@maxRankDelta > 1) OR (@minRankDelta < 1))
BEGIN
-- Raise an error if our input data sets rank column does not start at 1 or does not increment by 1 as expected.
RAISERROR (N'Attempting to add covariant group with invalid rank order.', -- Message text.
15, -- Severity,
1 -- State
); -- Second argument.
END
ELSE
BEGIN
-- Generate a new group ID
INSERT INTO [dbo].[CovariateGroupIDs]
(
[CreatedDateTime]
)
SELECT GETDATE() AS [CreatedDateTime]
SET @groupID = SCOPE_IDENTITY();
WITH CTE_GroupsCompareSize
AS
(
-- Compare the size of the new group with all of the existing groups. If the size is different we can
-- safely assume that the group is either a sub set or super set of the compared group. These groups
-- can be excluded from further consideration.
SELECT [CovariateGroups].[ID]
,[CovariateGroups].[Rank]
,[CovariateGroups].[Covariate]
,COUNT([CovariateGroups].[Rank]) OVER (PARTITION BY [CovariateGroups].[ID]) GroupSize
,@groupSize AS NewGroupSize
FROM [CovariateGroups]
)
,CTE_GroupsCompareRank
AS
(
-- For groups of the same size left outer join the new group on the original groups on both rank and covariant entry.
-- If the MIN() OVER window function return a value of 0 then there is at least on entry in the compared groups that does
-- not match and is therefore deemed different.
SELECT [OrginalGroup].[ID]
,[OrginalGroup].[Rank]
,[OrginalGroup].[Covariate]
,MIN(
CASE
WHEN [NewGroup].[Covariate] IS NULL THEN 0
ELSE 1
END
) OVER (PARTITION BY [OrginalGroup].[ID]) AS EntireGroupRankMatch
FROM CTE_GroupsCompareSize [OrginalGroup]
LEFT OUTER JOIN @covariateGroupEntry [NewGroup] ON ([OrginalGroup].[Rank] = [NewGroup].[Rank] AND [OrginalGroup].[Covariate] = [NewGroup].[Covariate])
WHERE GroupSize = NewGroupSize
)
SELECT @groupMatchCount = COUNT(EntireGroupRankMatch)
FROM CTE_GroupsCompareRank
WHERE EntireGroupRankMatch = 1
IF ISNULL(@groupMatchCount, 0) = 0
BEGIN
INSERT INTO [CovariateGroups]
(
[ID]
,[Rank]
,[Covariate]
)
SELECT @groupID AS [ID]
,[Rank]
,[Covariate]
FROM @covariateGroupEntry
END
ELSE
BEGIN
-- Raise an error if our uniqueness constraints are not met.
RAISERROR (N'Uniqueness contain violation, the covariant set is not unique with table "CovariateGroups".', -- Message text.
15, -- Severity,
1 -- State
); -- Second argument.
END
END
END
方法2 - 使用觸發器查看
第二種方法涉及使用視圖並在視圖上創建而不是插入觸發器。
首先,我們創建的視圖如下:
CREATE VIEW CovariateGroupsView
AS
SELECT [ID]
,[Rank]
,[Covariate]
FROM CovariateGroups
然後我們創建觸發器:
ALTER TRIGGER CovariateGroupsViewInsteadOfInsert on CovariateGroupsView
INSTEAD OF INSERT
AS
BEGIN
DECLARE @groupID INT;
DECLARE @groupSize INT;
DECLARE @groupMatchCount INT;
DECLARE @minRank INT;
DECLARE @maxRankDelta INT;
DECLARE @minRankDelta INT;
-- Get the size of the new group which user will attempt to add.
SELECT @groupSize = COUNT([Rank])
FROM inserted
-- Validate that the new group rank starts at 1 and increments by 1 step value only.
SELECT @minRank = ISNULL(MIN([Rank]), 0)
,@maxRankDelta = ISNULL(MAX(Delta), 0)
,@minRankDelta = ISNULL(MIN(Delta), 0)
FROM (
SELECT [Rank]
,[Rank] - (LAG([Rank], 1, 0) OVER (ORDER BY [Rank])) AS Delta
FROM inserted
) RankValidation
IF ((@minRank > 1) OR (@maxRankDelta > 1) OR (@minRankDelta < 1))
BEGIN
RAISERROR (N'Attempting to add covariant group with invalid rank order.', -- Message text.
15, -- Severity,
1 -- State
); -- Second argument.
END
ELSE
BEGIN
-- Generate a new group ID
INSERT INTO [dbo].[CovariateGroupIDs]
(
[CreatedDateTime]
)
SELECT GETDATE() AS [CreatedDateTime]
SET @groupID = SCOPE_IDENTITY();
WITH CTE_GroupsCompareSize
AS
(
-- Compare the size of the new group with all of the existing groups. If the size is different we can
-- safely assume that the group is either a sub set or super set of the compared group. These groups
-- can be excluded from further consideration.
SELECT [CovariateGroups].[ID]
,[CovariateGroups].[Rank]
,[CovariateGroups].[Covariate]
,COUNT([CovariateGroups].[Rank]) OVER (PARTITION BY [CovariateGroups].[ID]) GroupSize
,@groupSize AS NewGroupSize
FROM [CovariateGroups]
)
,CTE_GroupsCompareRank
AS
(
-- For groups of the same size left outer join the new group on the original groups on both rank and covariant entry.
-- If the MIN() OVER window function return a value of 0 then there is at least on entry in the compared groups that does
-- not match and is therefore deemed different.
SELECT [OrginalGroup].[ID]
,[OrginalGroup].[Rank]
,[OrginalGroup].[Covariate]
,MIN(
CASE
WHEN [NewGroup].[Covariate] IS NULL THEN 0
ELSE 1
END
) OVER (PARTITION BY [OrginalGroup].[ID]) AS EntireGroupRankMatch
FROM CTE_GroupsCompareSize [OrginalGroup]
LEFT OUTER JOIN inserted [NewGroup] ON ([OrginalGroup].[Rank] = [NewGroup].[Rank] AND [OrginalGroup].[Covariate] = [NewGroup].[Covariate])
WHERE GroupSize = NewGroupSize
)
SELECT @groupMatchCount = COUNT(EntireGroupRankMatch)
FROM CTE_GroupsCompareRank
WHERE EntireGroupRankMatch = 1
IF ISNULL(@groupMatchCount, 0) = 0
BEGIN
INSERT INTO [CovariateGroups]
(
[ID]
,[Rank]
,[Covariate]
)
SELECT @groupID AS [ID]
,[Rank]
,[Covariate]
FROM inserted
END
ELSE
BEGIN
RAISERROR (N'Uniqueness contain violation, the covariant set is not unique with table "CovariateGroups".', -- Message text.
15, -- Severity,
1 -- State
); -- Second argument.
END
END
END;
下面的例子顯示了存儲過程應該如何執行:
DECLARE @covariateGroupEntry AS dbo.CovariateGroupEntry
-- INSERT GROUP 1 -------------------
INSERT INTO @covariateGroupEntry
(
[Rank]
,[Covariate]
)
SELECT 1 ,'Age' UNION ALL
SELECT 2 ,'Gender' UNION ALL
SELECT 3 ,'YearOfBirth'
EXEC CovariateGroup_Add @covariateGroupEntry
以下示例顯示如何使用視圖插入組:
DECLARE @covariateGroupEntry AS dbo.CovariateGroupEntry
-- INSERT GROUP 1 -------------------
INSERT INTO @covariateGroupEntry
(
[Rank]
,[Covariate]
)
SELECT 1 ,'Age' UNION ALL
SELECT 2 ,'Gender' UNION ALL
SELECT 3 ,'YearOfBirth'
INSERT INTO [dbo].[CovariateGroupsView]
(
[Rank]
,[Covariate]
)
SELECT [Rank]
,[Covariate]
FROM @covariateGroupEntry
DELETE @covariateGroupEntry -- Delete our memory table if we intend to use it again.
一般來說,我會避免使用視圖方法,因爲它會比存儲過程更容易出現邊緣情況,並且可能會出現一些意外的行爲。以下調用示例:
INSERT INTO [dbo].[CovariateGroupsView]
(
[Rank]
,[Covariate]
)
SELECT 1 ,'Age' UNION ALL
SELECT 2 ,'Gender' UNION ALL
SELECT 3 ,'YearOfBirth'
由於視圖上的觸發器將每行視爲單獨的數據集/組,因此無法按預期工作。結果驗證檢查將失敗。
我假設行列必須連續並從'1'開始?否則,僅插入性別但等級爲2的規則似乎既被您的規則所禁止也被允許...... –
正確。等級從1開始,對於組中的每個協變量都加1。我應該指出這一點。 – mortysporty