เพิ่มข้อ จำกัด ที่ไม่ซ้ำกันในการรวมสองคอลัมน์


149

ฉันมีโต๊ะแล้วบุคคลคนเดียวกันก็เข้ามาในPersonโต๊ะของฉันสองครั้ง ตอนนี้คีย์หลักเป็นเพียงตัวเลขอัตโนมัติ แต่มีอีกสองฟิลด์ที่ฉันต้องการบังคับให้เป็นเอกลักษณ์

ตัวอย่างเช่นฟิลด์คือ:

ID  
Name  
Active  
PersonNumber  

ฉันต้องการเพียง 1 ระเบียนที่มี PersonNumber และ Active = 1 ที่ไม่ซ้ำกัน
(ดังนั้นการรวมกันของทั้งสองฟิลด์จะต้องไม่ซ้ำกัน)

เป็นวิธีที่ดีที่สุดในตารางที่มีอยู่ในเซิร์ฟเวอร์ SQL ที่ฉันสามารถทำได้ดังนั้นถ้าใครแทรกมีค่าเดียวกันกับค่าที่มีอยู่มันล้มเหลวดังนั้นฉันไม่ต้องกังวลเกี่ยวกับเรื่องนี้ในรหัสแอปพลิเคชันของฉัน


3
คุณยังต้องกังวลในรหัสแอปพลิเคชันของคุณ
Dan Bracuk

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 การละเมิดข้อ จำกัด คีย์ UNIQUE 'uq_Person' ไม่สามารถแทรกคีย์ซ้ำในวัตถุ 'dbo.Person' คำสั่งถูกยกเลิก


3
@leora ใช่ผมค่อนข้างมั่นใจว่าข้อเสนอคำตอบของฉันที่มีเอกลักษณ์ในสองคอลัมน์
Aaron Bertrand

2
ไม่ใช่ว่าแต่ละคอลัมน์จะต้องไม่ซ้ำกันการรวมกัน (การต่อข้อมูล) ของคอลัมน์จะต้องไม่ซ้ำกัน ไม่สมเหตุสมผล .
leora

14

สิ่งนี้สามารถทำได้ใน GUI:

  1. ใต้ตาราง "บุคคล" คลิกขวาที่ดัชนี
  2. คลิก / โฮเวอร์ดัชนีใหม่
  3. คลิกดัชนีที่ไม่ทำคลัสเตอร์ ...

ป้อนคำอธิบายรูปภาพที่นี่

  1. ชื่อดัชนีเริ่มต้นจะได้รับ แต่คุณอาจต้องการเปลี่ยนชื่อ
  2. ตรวจสอบที่ไม่ซ้ำช่องทำเครื่องหมาย
  3. คลิกAdd ...ปุ่ม

ป้อนคำอธิบายรูปภาพที่นี่

  1. ตรวจสอบคอลัมน์ที่คุณต้องการรวม

ป้อนคำอธิบายรูปภาพที่นี่

  1. คลิกตกลงในแต่ละหน้าต่าง

1
ข้อ จำกัด ที่ไม่ซ้ำกันและดัชนีที่ไม่ซ้ำแตกต่างกันอย่างไร เพราะเมื่อคุณตั้งค่าข้อ จำกัด ที่ไม่ซ้ำมันมีข้อ จำกัด 900 ไบต์ แต่ดูเหมือนว่าดัชนีที่ไม่ซ้ำไม่มี
batmaci

2
ไม่มีอะไรดูบทความนี้สำหรับการอ้างอิง: blog.sqlauthority.com/2007/04/26/…
Eli

ฉันnew Indexไม่สามารถคลิกได้มันถูกปิดการใช้งาน :(
Faisal

4
@Faisal ปิดหน้าต่างผลลัพธ์ / การออกแบบที่คุณเปิดสำหรับตารางนั้นแล้วลองอีกครั้ง
KalaNag

@Faisal ตรวจสอบสิ่งนี้: stackoverflow.com/a/60014466/4654957
Diego Venâncio

3

ในกรณีของฉันฉันต้องอนุญาตการไม่ใช้งานจำนวนมากและใช้งานได้เพียงปุ่มเดียวเท่านั้นเมื่อใช้งานร่วมกันเช่นนี้

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

และถ้าคุณมีข้อความค้นหาแทรกจำนวนมาก แต่ไม่ต้องการรับข้อความข้อผิดพลาดทุกครั้งคุณสามารถทำได้:

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.