Fügen Sie der Kombination zweier Spalten eine eindeutige Einschränkung hinzu


149

Ich habe einen Tisch und irgendwie ist dieselbe Person Personzweimal an meinen Tisch gekommen. Im Moment ist der Primärschlüssel nur eine automatische Nummerierung, aber es gibt zwei andere Felder, die ich erzwingen möchte, um eindeutig zu sein.

Zum Beispiel sind die Felder:

ID  
Name  
Active  
PersonNumber  

Ich möchte nur 1 Datensatz mit einer eindeutigen PersonNumber und Active = 1.
(Die Kombination der beiden Felder muss also eindeutig sein.)

Was ist der beste Weg für eine vorhandene Tabelle in SQL Server? Wenn jemand anderes eine Einfügung mit demselben Wert wie ein vorhandener Wert ausführt, schlägt dies fehl, sodass ich mich in meinem Anwendungscode nicht darum kümmern muss.


3
Sie müssen sich in Ihrem Anwendungscode noch darum kümmern.
Dan Bracuk

2
Richtig, "mach dir keine Sorgen" bedeutet was? Wenn ein Benutzer versucht , ein Duplikat einzufügen, und SQL Server dies nicht tut, möchten Sie es ihm nicht mitteilen? Klingt so, als müsste sich die Anwendung darum kümmern.
Aaron Bertrand

Antworten:


219

Sobald Sie Ihre Duplikate entfernt haben:

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

oder

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

Natürlich kann es oft besser sein, zuerst nach dieser Verletzung zu suchen, bevor SQL Server versucht, die Zeile einzufügen und eine Ausnahme zurückzugeben (Ausnahmen sind teuer).

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/

Wenn Sie verhindern möchten, dass Ausnahmen in die Anwendung gelangen, ohne Änderungen an der Anwendung vorzunehmen, können Sie einen INSTEAD OFAuslöser verwenden:

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

Wenn Sie dem Benutzer jedoch nicht mitteilen, dass er die Einfügung nicht durchgeführt hat, werden Sie sich fragen, warum die Daten nicht vorhanden sind und keine Ausnahme gemeldet wurde.


BEARBEITEN hier ist ein Beispiel, das genau das tut, wonach Sie fragen, auch wenn Sie dieselben Namen wie Ihre Frage verwenden, und dies beweist. Sie sollten es ausprobieren, bevor Sie davon ausgehen, dass die obigen Ideen nur die eine oder andere Spalte im Gegensatz zur Kombination behandeln ...

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

Daten in der Tabelle nach all dem:

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

Fehlermeldung beim letzten Einfügen:

Meldung 2627, Ebene 14, Status 1, Zeile 3 Verletzung der EINZIGARTIGEN SCHLÜSSEL-Einschränkung 'uq_Person'. Doppelter Schlüssel kann nicht in Objekt 'dbo.Person' eingefügt werden. Die Anweisung wurde beendet.


3
@leora ja, ich bin mir ziemlich sicher, dass meine Antwort die Eindeutigkeit in zwei Spalten betrifft .
Aaron Bertrand

2
Es ist nicht so, dass jede Spalte eindeutig sein muss, sondern dass die Kombination (Verkettung) von Spalten eindeutig sein muss. Ist das sinnvoll . .
Leora

14

Dies kann auch in der GUI erfolgen:

  1. Klicken Sie unter der Tabelle "Person" mit der rechten Maustaste auf Indizes
  2. Klicken Sie auf Neuen Index
  3. Klicken Sie auf Nicht-Clustered-Index ...

Geben Sie hier die Bildbeschreibung ein

  1. Ein Standard - Indexname wird gegeben , aber Sie können es ändern möchten.
  2. Überprüfen Einzigartige Checkbox
  3. Klicken Sie auf die Schaltfläche Hinzufügen ...

Geben Sie hier die Bildbeschreibung ein

  1. Überprüfen Sie die gewünschten Spalten

Geben Sie hier die Bildbeschreibung ein

  1. Klicken Sie in jedem Fenster auf OK .

1
Was ist der Unterschied zwischen Unique Constraint und Unique Index? Denn wenn Sie die eindeutige Einschränkung festlegen, gilt eine Beschränkung von 900 Byte, aber es sieht so aus, als ob der eindeutige Index keine Beschränkung hat.
Batmaci

2
Nichts Siehe diesen Artikel als Referenz: blog.sqlauthority.com/2007/04/26/…
Eli

Mein new Indexist nicht anklickbar, es ist deaktiviert :(
Faisal

4
@Faisal Schließen Sie die Ergebnis- / Designfenster, die Sie für diese Tabelle geöffnet haben, und versuchen Sie es erneut.
KalaNag

@Faisal überprüfen Sie dies: stackoverflow.com/a/60014466/4654957
Diego Venâncio

3

In meinem Fall musste ich viele inaktive und nur eine Kombination von zwei aktiven Tasten wie folgt zulassen:

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

Das scheint zu funktionieren:

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

Hier sind meine Testfälle:

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

Ich schätze dich, dass du deinen Testfall hier aufgenommen hast. Dies ist eine bewährte Methode. Ich würde mir wünschen, dass weitere Antworten zum Stapelüberlauf übernommen werden.
Jeremy Caney

0

Und wenn Sie viele Einfügeabfragen haben, aber nicht jedes Mal eine FEHLER-Nachricht senden möchten, können Sie dies tun:

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

Geben Sie hier die Bildbeschreibung ein

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.