Thêm ràng buộc duy nhất cho sự kết hợp của hai cột


149

Tôi có một cái bàn và, bằng cách nào đó, cùng một người đã vào Personbàn của tôi hai lần. Ngay bây giờ, khóa chính chỉ là một số tự động nhưng có hai trường khác tồn tại mà tôi muốn buộc phải là duy nhất.

Ví dụ: các trường là:

ID  
Name  
Active  
PersonNumber  

Tôi chỉ muốn 1 bản ghi với PersonNumber và Active = 1.
(Vì vậy, sự kết hợp của hai trường cần phải là duy nhất)

Cách tốt nhất trên bảng hiện có trong máy chủ SQL là gì Tôi có thể tạo ra nó để nếu bất kỳ ai khác thực hiện thao tác chèn có cùng giá trị với giá trị hiện tại, thì nó không thành công nên tôi không phải lo lắng về điều này trong mã ứng dụng của mình.


3
Bạn vẫn phải lo lắng về nó trong mã ứng dụng của bạn.
Dan Bracuk

2
Phải, "không phải lo lắng về nó" có nghĩa là gì? Nếu người dùng cố gắng chèn một bản sao và SQL Server không làm điều đó, bạn có muốn nói với họ không? Âm thanh như ứng dụng cần phải lo lắng về nó.
Aaron Bertrand

Câu trả lời:


219

Khi bạn đã xóa (các) bản sao của mình:

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

hoặc là

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

Tất nhiên, thường có thể tốt hơn để kiểm tra vi phạm này trước, trước khi chỉ để SQL Server thử chèn hàng và trả lại một ngoại lệ (ngoại lệ rất tốn kém).

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

http://www.mssqltips.com/sqlservertip / 2632

Nếu bạn muốn ngăn các trường hợp ngoại lệ xuất hiện trên ứng dụng, mà không thực hiện thay đổi cho ứng dụng, bạn có thể sử dụng trình INSTEAD OFkích hoạt:

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

Nhưng nếu bạn không nói với người dùng rằng họ không thực hiện thao tác chèn, họ sẽ tự hỏi tại sao dữ liệu không có ở đó và không có ngoại lệ nào được báo cáo.


EDIT ở đây là một ví dụ thực hiện chính xác những gì bạn yêu cầu, thậm chí sử dụng cùng tên với câu hỏi của bạn và chứng minh điều đó. Bạn nên dùng thử trước khi cho rằng các ý tưởng trên chỉ coi một cột này hoặc cột kia trái ngược với sự kết hợp ...

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

Dữ liệu trong bảng sau tất cả những điều này:

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

Thông báo lỗi ở lần chèn cuối cùng:

Msg 2627, Cấp 14, Tiểu bang 1, Dòng 3 Vi phạm ràng buộc KHÓA CHÍNH HÃNG 'uq_Person'. Không thể chèn khóa trùng lặp vào đối tượng 'dbo.Person'. Các tuyên bố này đã bị chấm dứt.


3
@leora có, tôi khá chắc chắn câu trả lời của tôi liên quan đến tính độc đáo trên hai cột .
Aaron Bertrand

2
Không phải mỗi cột phải là duy nhất, sự kết hợp (nối) của các cột phải là duy nhất. Điều đó có ý nghĩa . .
leora

14

Điều này cũng có thể được thực hiện trong GUI:

  1. Dưới bảng "Người", nhấp chuột phải Chỉ mục
  2. Nhấp / di chuột Chỉ mục mới
  3. Nhấp vào Chỉ mục không được nhóm ...

nhập mô tả hình ảnh ở đây

  1. Tên chỉ mục mặc định sẽ được cung cấp nhưng bạn có thể muốn thay đổi tên.
  2. Kiểm tra duy nhất hộp kiểm
  3. Nhấp vào Thêm nút ...

nhập mô tả hình ảnh ở đây

  1. Kiểm tra các cột bạn muốn bao gồm

nhập mô tả hình ảnh ở đây

  1. Nhấn OK trong mỗi cửa sổ.

1
sự khác biệt giữa ràng buộc duy nhất và chỉ số duy nhất là gì? Bởi vì khi bạn đặt Hạn chế duy nhất, nó có giới hạn 900 Byte nhưng có vẻ như Unique Index không có.
batmaci

2
Không có gì Xem bài viết này để tham khảo: blog.sqlauthority.com/2007/04/26/ từ
Eli

My new Indexkhông thể nhấp được, nó bị vô hiệu hóa :(
Faisal

4
@Faisal đóng kết quả / thiết kế cửa sổ bạn đã mở cho bảng đó và thử lại.
KalaNag

@Faisal kiểm tra điều này: stackoverflow.com/a/60014466/4654957
Diego Venâncio

3

Trong trường hợp của tôi, tôi cần cho phép nhiều hoạt động và chỉ một tổ hợp hai phím hoạt động, như thế này:

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

Điều này dường như làm việc:

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

Dưới đây là các trường hợp thử nghiệm của tôi:

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

Tôi đánh giá cao bạn bao gồm cả trường hợp thử nghiệm của bạn ở đây. Đó là cách thực hành tốt nhất tôi muốn xem thêm câu trả lời Stack Overflow.
Jeremy Caney

0

Và nếu bạn có nhiều truy vấn chèn nhưng không muốn nhận thông báo LRI mỗi lần, bạn có thể thực hiện:

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

nhập mô tả hình ảnh ở đây

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.