2016-05-13 7 views
3

私は共変量が同じグループで一緒に属しているので、SQL Serverでは複数行の一意性が可能ですか?

ID Rank Covariate 
1 1 Age 
1 2 Gender 
1 3 YearOfBirth 
2 1 Gender 

IDキャプチャのような共変量goupingを定義しようとするテーブルを持っています。したがって、共変量群1(ID = 1)は年齢、性別、生年月日で構成され、2群は性別のみで構成されます。

ここで、性別のみで構成される新しい共変量群を挿入することは、このグループが既に存在するため、不正であるはずですが、AgeとGenderで構成される新しいグループを挿入する必要があります(グループ1のサブセットです。完全一致)。

ランクの事項はそう

ID Rank Covariate 
     2 Age 
     1 Gender 
     3 YearOfBirth 

グループ1

に等しいとみなされるべきではないが、SQLサーバーにこれを強制する方法はありますか?

理想的には、ID列は正当な挿入物に自動的に追加されます(ただし、別の問題があります)。

+0

ランクは連続していて、「1」から始める必要がありますか?そうでなければ、性別の挿入だけが、ランク2で禁止され、あなたのルールによって許可されているようです... –

+0

真。ランクは1から始まり、グループ内の共変量ごとに1ずつ増加します。私はそれを指定するべきだった。 – mortysporty

答えて

1

私は共変を強制するためにあらゆる手段を知りませんグループの一意性基準を、標準的な一意性制約またはチェック制約、または他のどのような優雅な解決策によっても解決できます。ただし、ストアド・プロシージャまたは「INSTEAD OF INSERT」トリガーが定義されたビューを介してのみ、表へのアクセスを許可することで、制約を適用することができます。

方法1 - 次の例では、ストアドプロシージャ方法を示す手順

ストアド。最初に、テーブル変数型を作成して、共変量グループを読み取り専用パラメータとしてストアドプロシージャに渡すことができます。

CREATE TABLE CovariateGroupIDs 
    (
     [GroupID] INT PRIMARY KEY IDENTITY 
     ,[CreatedDateTime] DATETIME NOT NULL 
    ) 
:私たちは私たちのIDを自動生成するために使用されるダミーのテーブルを作成し

CREATE TABLE CovariateGroups 
    (
     [ID]   INT NOT NULL 
     ,[Rank]   INT NOT NULL 
     ,[Covariate] NVARCHAR(50) 
     PRIMARY KEY([ID], [Rank], [Covariate]) 
    ) 

次のステップ:

CREATE TYPE CovariateGroupEntry AS TABLE 
    (
     [Rank]   INT NOT NULL 
     ,[Covariate] NVARCHAR(50) 
     PRIMARY KEY([Rank], [Covariate]) 
    ) 

次は、私たちは共変グループが含まれています私たちのベーステーブルを作成します

手順の最終ステップ:

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 - トリガーで表示する

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. 

一般に、ストアドプロシージャより多くのエッジケースの影響を受けやすく、予期しない動作が発生する可能性があるため、ビューメソッドを使用しないでください。

ビュー上のトリガーは、すべての行を別々のデータセット/グループとして扱うため、期待通りに機能しません。その結果、検証チェックは失敗します。

+0

あなたは教師と紳士です。それは完璧に働いた!私は遅い返信をお詫びします(5月16日と17日はノルウェーの祝日でした。私は昨日私の皿にたくさんのものを持っていました)。私はあなたが本当にこれを解決するために真剣な努力をしていることがわかります。ほんとうにありがとう。 @EastOfJupiterにも感謝します。私はこの1つに行きましたが、私はまたこれにあなたの努力を感謝します。 – mortysporty

1

複数の行にわたって繰り返される強制的な一意制約を生成する方法がないことは明らかです。繰り返しすると一意ではないためです。

ただし、共変量値のグループ化が複数回挿入されないようにする簡単なチェックを作成するには、巧妙な方法があります。簡略化の観点から

SQLは、2つの列生成する下記

ID GroupCovariate 
1 Age, Gender, YearOfBirth 
2 Gender 
以下の通りSELECTの結果である

CREATE TABLE #tmp_Covariate (ID INT, RANK INT, Covariate VARCHAR(24)) 

INSERT INTO #tmp_Covariate (ID, RANK, Covariate) 
VALUES (1,1,'Age') 
     ,(1,2,'Gender') 
     ,(1,3,'YearOfBirth') 
     ,(2,1,'Gender') 

SELECT DISTINCT ID 
     ,STUFF((SELECT N', ' + CAST(C2.[Covariate] AS VARCHAR(256)) 
        FROM #tmp_Covariate C2 
       WHERE C1.ID = C2.ID 
       ORDER 
        BY C2.ID,C2.RANK 
        FOR XML PATH ('')),1,2,'') AS GroupCovariate 
FROM #tmp_Covariate C1 

:ID、及び共変量値の順序のoccuranceは

第三のグループは、共変量値はテーブルに追加された場合:

ID Rank Covariate 
     2 Age 
     1 Gender 
     3 YearOfBirth 

次に、共変量の順序付きの出現は、上記で返されたGroupCovariateの列と一致しません。

私がこれを解決していたら、テーブル値のパラメータを受け入れる関数を作成します。正常にコミットされた場合に表示されるように、テーブルに対してチェックする必要のある入力をテーブルに入力します。

DECLARE @TVP TABLE (Rank INT, Covariate VARCHAR(24)) 

INSERT INTO @TVP(Rank, Covariate) VALUES (1,'Age'),(2,'Gender'),(3,'YearOfBirth') 

SELECT COUNT(CheckTable.GroupCovariate) AS Exist 
FROM  (SELECT STUFF((SELECT N', ' + CAST(C2.[Covariate] AS VARCHAR(256)) 
      FROM @TVP C2 
      ORDER 
       BY C2.RANK 
      FOR XML PATH ('')),1,2,'') AS GroupCovariate 
     ) AS InputTable 
JOIN  (SELECT DISTINCT ID 
       ,STUFF((SELECT N', ' + CAST(C2.[Covariate] AS VARCHAR(256)) 
      FROM #tmp_Covariate C2 
      WHERE C1.ID = C2.ID 
      ORDER 
      BY C2.ID,C2.RANK 
      FOR XML PATH ('')),1,2,'') AS GroupCovariate 
      FROM #tmp_Covariate C1) AS CheckTable 
    ON  CheckTable.GroupCovariate = InputTable.GroupCovariate 

共変量の供給グループがすでにテーブルに存在するため、出力が1(なしグループが存在しないない場合はtrue、またはfalseのために0ブール値として返すことができる)であろう。

Exist 
1 

私は私のcovariantsの一環として、 "FavoriteColor" を供給した場合:

DECLARE @TVP TABLE (Rank INT, Covariate VARCHAR(24)) 

INSERT INTO @TVP(Rank, Covariate) VALUES (1,'FavoriteColor'),(2,'Gender'),(3,'YearOfBirth') 

SELECT COUNT(CheckTable.GroupCovariate) AS Exist 
FROM  (SELECT STUFF((SELECT N', ' + CAST(C2.[Covariate] AS VARCHAR(256)) 
      FROM @TVP C2 
      ORDER 
       BY C2.RANK 
      FOR XML PATH ('')),1,2,'') AS GroupCovariate 
     ) AS InputTable 
JOIN  (SELECT DISTINCT ID 
       ,STUFF((SELECT N', ' + CAST(C2.[Covariate] AS VARCHAR(256)) 
      FROM #tmp_Covariate C2 
      WHERE C1.ID = C2.ID 
      ORDER 
      BY C2.ID,C2.RANK 
      FOR XML PATH ('')),1,2,'') AS GroupCovariate 
      FROM #tmp_Covariate C1) AS CheckTable 
    ON  CheckTable.GroupCovariate = InputTable.GroupCovariate 

を私の結果は0である:

Exist 
0 
+0

ありがとう!私は@ Edmond Quintonが投稿したバージョンを選択しましたが、私はあなたにこれに手伝ってくれることに深刻な努力を払うと言っています。本当に感謝しています。また、遅れて返答するためのappologies。 – mortysporty

+0

@mortysportyの心配はありません。私の冒頭で述べたように、これを行う方法はたくさんあります。私の意見では、これは最も簡単で再利用可能な方法でした(テーブルデータを変更するすべてのprocにロジックを記述する必要はありません)。最初にサニティチェック機能を投げれば完了です。これらのルールが突然変更された場合は、関数のみを変更する必要があります。私はその種のシンプルさが個人的に好きです:) – EastOfJupiter

関連する問題