Tambahkan batasan unik ke kombinasi dua kolom

149

Saya punya meja dan, entah bagaimana, orang yang sama masuk ke Personmeja saya dua kali. Saat ini, kunci utama hanya nomor otomatis tetapi ada dua bidang lain yang ada yang ingin saya paksa menjadi unik.

Misalnya, bidangnya adalah:

ID  
Name  
Active  
PersonNumber  

Saya hanya ingin 1 catatan dengan PersonNumber yang unik dan Aktif = 1.
(Jadi kombinasi dari kedua bidang harus unik)

Apa cara terbaik pada tabel yang ada di SQL server saya bisa membuatnya jadi jika orang lain melakukan penyisipan dengan nilai yang sama dengan nilai yang ada, gagal jadi saya tidak perlu khawatir tentang hal ini dalam kode aplikasi saya.

leora
sumber
3
Anda masih harus khawatir tentang hal itu dalam kode aplikasi Anda.
Dan Bracuk
2
Benar, "tidak perlu khawatir" artinya apa? Jika pengguna mencoba untuk memasukkan duplikat, dan SQL Server tidak melakukannya, tidakkah Anda ingin memberi tahu mereka? Sepertinya aplikasi perlu khawatir tentang itu.
Aaron Bertrand

Jawaban:

219

Setelah Anda menghapus duplikat Anda:

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

atau

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

Tentu saja, sering kali lebih baik untuk memeriksa pelanggaran ini terlebih dahulu, sebelum membiarkan SQL Server mencoba memasukkan baris dan mengembalikan pengecualian (pengecualian mahal).

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/

Jika Anda ingin mencegah pengecualian meluap ke aplikasi, tanpa membuat perubahan pada aplikasi, Anda dapat menggunakan INSTEAD OFpemicu:

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

Tetapi jika Anda tidak memberi tahu pengguna bahwa mereka tidak melakukan penyisipan, mereka akan bertanya-tanya mengapa data tidak ada dan tidak ada pengecualian yang dilaporkan.


EDIT di sini adalah contoh yang melakukan persis apa yang Anda minta, bahkan menggunakan nama yang sama dengan pertanyaan Anda, dan membuktikannya. Anda harus mencobanya sebelum mengasumsikan ide-ide di atas hanya memperlakukan satu kolom atau yang lain sebagai lawan kombinasi ...

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

Data dalam tabel setelah semua ini:

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

Pesan kesalahan pada sisipan terakhir:

Msg 2627, Level 14, Negara Bagian 1, Baris 3 Pelanggaran batasan UNIQUE KEY 'uq_Person'. Tidak dapat memasukkan kunci duplikat di objek 'dbo.Person'. Pernyataan itu telah dihentikan.

Aaron Bertrand
sumber
3
@lea ya, saya cukup yakin jawaban saya berkaitan dengan keunikan pada dua kolom .
Aaron Bertrand
2
ini bukan berarti bahwa setiap kolom harus unik, tetapi kombinasi (gabungan) dari kolom harus unik. Apakah itu masuk akal . .
leora
14

Ini juga dapat dilakukan di GUI:

  1. Di bawah tabel "Person", klik kanan Indexes
  2. Klik / arahkan Indeks Baru
  3. Klik Non-Clustered Index ...

masukkan deskripsi gambar di sini

  1. Nama Indeks default akan diberikan tetapi Anda mungkin ingin mengubahnya.
  2. Centang kotak centang Unik
  3. Klik tombol Tambah ...

masukkan deskripsi gambar di sini

  1. Periksa kolom yang ingin Anda sertakan

masukkan deskripsi gambar di sini

  1. Klik OK di setiap jendela.
Tony L.
sumber
1
apa perbedaan antara Unique Constraint dan Unique Index? Karena ketika Anda menetapkan Batasan Unik, ia memiliki batasan 900 Bytes tetapi Sepertinya Indeks Unik tidak memilikinya.
batmaci
2
Tidak Ada Lihat artikel ini untuk referensi: blog.sqlauthority.com/2007/04/26/…
Eli
My new Indextidak dapat diklik itu dinonaktifkan :(
Faisal
4
@Faisal menutup jendela hasil / desain yang Anda buka untuk tabel itu dan coba lagi.
KalaNag
@Faisal periksa ini: stackoverflow.com/a/60014466/4654957
Diego Venâncio
3

Dalam kasus saya, saya perlu mengizinkan banyak tidak aktif dan hanya satu kombinasi dari dua kunci yang aktif, seperti ini:

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

Ini sepertinya berhasil:

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

Berikut ini adalah kasus pengujian saya:

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
pengguna3816689
sumber
Saya menghargai Anda termasuk test case Anda di sini. Itu praktik terbaik yang saya ingin melihat lebih banyak jawaban Stack Overflow diadopsi.
Jeremy Caney
0

Dan jika Anda memiliki banyak kueri insert tetapi tidak ingin mengirim pesan ERROR setiap kali, Anda dapat melakukannya:

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

masukkan deskripsi gambar di sini

Diego Venâncio
sumber