2つの列の組み合わせに一意の制約を追加する


149

テーブルがあり、どういうわけか、同じ人が私のPersonテーブルに2回入りました。現在、主キーは単なる自動番号ですが、他の2つのフィールドを使用して、一意にする必要があります。

たとえば、フィールドは次のとおりです。

ID  
Name  
Active  
PersonNumber  

一意のPersonNumberおよびActive = 1の1つのレコードのみが必要です
(したがって、2つのフィールドの組み合わせは一意である必要があります)

SQLサーバーの既存のテーブルで最善の方法は何ですか?既存の値と同じ値を使用して他の人が挿入を行った場合、失敗するので、アプリケーションコードでこれを心配する必要はありません。


3
あなたはまだあなたのアプリケーションコードでそれについて心配する必要があります。
Dan Bracuk 2013

2
そう、「心配しなくていい」とはどういう意味ですか?ユーザーが重複を挿入しようとしたが、SQL Serverがそれを行わなかった場合、そのことを伝えたくありませんか?アプリケーションはそれについて心配する必要があるように聞こえます。
Aaron Bertrand

回答:


219

重複を削除したら:

ALTER TABLE dbo.yourtablename
  ADD CONSTRAINT uq_yourtablename UNIQUE(column1, column2);

または

CREATE UNIQUE INDEX uq_yourtablename
  ON dbo.yourtablename(column1, column2);

もちろん、SQL Serverに行を挿入させて例外を返すようにさせる前に、最初にこの違反をチェックする方が良い場合があります(例外は高コストです)。

http://www.sqlperformance.com/2012/08/t-sql-queries/error-handling

http://www.mssqltips.com/sqlservertip/2632/checking-for-potential-constraint-violations-before-entering-sql-server-try-and-catch-logic/

アプリケーションに変更を加えずに、例外がアプリケーションにバブリングしないようにする場合は、INSTEAD OFトリガーを使用できます。

CREATE TRIGGER dbo.BlockDuplicatesYourTable
 ON dbo.YourTable
 INSTEAD OF INSERT
AS
BEGIN
  SET NOCOUNT ON;

  IF NOT EXISTS (SELECT 1 FROM inserted AS i 
    INNER JOIN dbo.YourTable AS t
    ON i.column1 = t.column1
    AND i.column2 = t.column2
  )
  BEGIN
    INSERT dbo.YourTable(column1, column2, ...)
      SELECT column1, column2, ... FROM inserted;
  END
  ELSE
  BEGIN
    PRINT 'Did nothing.';
  END
END
GO

しかし、挿入を実行しなかったとユーザーに伝えないと、なぜデータが存在せず、例外が報告されなかったのか不思議に思うでしょう。


ここでの編集は、質問と同じ名前を使用していても、あなたが求めていることを正確に実行し、それを証明する例です。上記のアイデアは、組み合わせではなく、どちらか一方の列のみを扱うと想定する前に、試してみてください...

USE tempdb;
GO

CREATE TABLE dbo.Person
(
  ID INT IDENTITY(1,1) PRIMARY KEY,
  Name NVARCHAR(32),
  Active BIT,
  PersonNumber INT
);
GO

ALTER TABLE dbo.Person 
  ADD CONSTRAINT uq_Person UNIQUE(PersonNumber, Active);
GO

-- succeeds:
INSERT dbo.Person(Name, Active, PersonNumber)
  VALUES(N'foo', 1, 22);
GO

-- succeeds:
INSERT dbo.Person(Name, Active, PersonNumber)
  VALUES(N'foo', 0, 22);
GO

-- fails:
INSERT dbo.Person(Name, Active, PersonNumber)
  VALUES(N'foo', 1, 22);
GO

このすべての後の表のデータ:

ID   Name   Active PersonNumber
---- ------ ------ ------------
1    foo    1      22
2    foo    0      22

最後の挿入時のエラーメッセージ:

メッセージ2627、レベル14、状態1、行3一意キー制約 'uq_Person'の違反。オブジェクト 'dbo.Person'に重複するキーを挿入することはできません。ステートメントは終了されました。


3
@leoraはい、私の答えは2つの列の一意性を扱っていると確信しています
アーロンベルトラン

2
各列が一意である必要はなく、列の組み合わせ(連結)は一意である必要があります。それは理にかなっていますか 。。
leora 2013

14

これはGUIでも実行できます。

  1. テーブル「Person」の下で、「Indexes」を右クリックします
  2. 新しいインデックスをクリック/ホバー
  3. [ 非クラスター化インデックス...]をクリックします。

ここに画像の説明を入力してください

  1. デフォルトのインデックス名が指定されますが、変更することもできます。
  2. チェックユニークなチェックボックスを
  3. [ 追加... ]ボタンをクリックします

ここに画像の説明を入力してください

  1. 含める列を確認してください

ここに画像の説明を入力してください

  1. 各ウィンドウで[ OK]をクリックします。

1
一意の制約と一意のインデックスの違いは何ですか?一意の制約を設定すると、900バイトの制限がありますが、一意のインデックスにはないように見えます。
batmaci 16

2
何も参照のために、この記事を参照:blog.sqlauthority.com/2007/04/26/...
イーライ

new Indexはクリックできません。無効になっています:(
Faisal

4
@Faisalは、そのテーブルに対して開いている結果/設計ウィンドウを閉じて、再試行します。
KalaNag

@ファイサルはこれをチェックしてください:stackoverflow.com/a/60014466/4654957
ディエゴ

3

私の場合、次のように、多くの非アクティブと2つのキーの1つの組み合わせのみをアクティブにする必要があります。

UUL_USR_IDF  UUL_UND_IDF    UUL_ATUAL
137          18             0
137          19             0
137          20             1
137          21             0

これはうまくいくようです:

CREATE UNIQUE NONCLUSTERED INDEX UQ_USR_UND_UUL_USR_IDF_UUL_ATUAL
ON USER_UND(UUL_USR_IDF, UUL_ATUAL)
WHERE UUL_ATUAL = 1;

これが私のテストケースです:

SELECT * FROM USER_UND WHERE UUL_USR_IDF = 137

insert into USER_UND values (137, 22, 1) --I CAN NOT => Cannot insert duplicate key row in object 'dbo.USER_UND' with unique index 'UQ_USR_UND_UUL_USR_IDF_UUL_ATUAL'. The duplicate key value is (137, 1).
insert into USER_UND values (137, 23, 0) --I CAN
insert into USER_UND values (137, 24, 0) --I CAN

DELETE FROM USER_UND WHERE UUL_USR_ID = 137

insert into USER_UND values (137, 22, 1) --I CAN
insert into USER_UND values (137, 27, 1) --I CAN NOT => Cannot insert duplicate key row in object 'dbo.USER_UND' with unique index 'UQ_USR_UND_UUL_USR_IDF_UUL_ATUAL'. The duplicate key value is (137, 1).
insert into USER_UND values (137, 28, 0) --I CAN
insert into USER_UND values (137, 29, 0) --I CAN

テストケースをここに含めていただきありがとうございます。これは、スタックオーバーフローの回答をさらに採用してもらいたいベストプラクティスです。
Jeremy Caney

0

そして、たくさんの挿入クエリがあっても毎回ERRORメッセージを取得したくない場合は、それを行うことができます:

CREATE UNIQUE NONCLUSTERED INDEX SK01 ON dbo.Person(ID,Name,Active,PersonNumber) 
WITH(IGNORE_DUP_KEY = ON)

ここに画像の説明を入力してください

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.