Sqlserver
 sql >> Database >  >> RDS >> Sqlserver

Aggiungi un vincolo univoco alla combinazione di due colonne

Dopo aver rimosso i tuoi duplicati:

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

o

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

Ovviamente, spesso può essere meglio controllare prima questa violazione, prima di lasciare che SQL Server tenti di inserire la riga e restituire un'eccezione (le eccezioni sono costose).

  • Impatto sulle prestazioni delle diverse tecniche di gestione degli errori

  • Verifica di potenziali violazioni dei vincoli prima di accedere a TRY/CATCH

Se vuoi evitare che le eccezioni si riversino nell'applicazione, senza apportare modifiche all'applicazione, puoi utilizzare un INSTEAD OF trigger:

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

Ma se non dici all'utente che non ha eseguito l'inserimento, si chiederà perché i dati non sono presenti e non è stata segnalata alcuna eccezione.

MODIFICA ecco un esempio che fa esattamente quello che stai chiedendo, anche usando gli stessi nomi della tua domanda, e lo dimostra. Dovresti provarlo prima di presumere che le idee di cui sopra trattino solo una colonna o l'altra invece della combinazione...

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

I dati nella tabella dopo tutto questo:

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

Messaggio di errore sull'ultimo inserto:

Msg 2627, Livello 14, Stato 1, Riga 3 Violazione del vincolo CHIAVE UNICA 'uq_Person'. Impossibile inserire una chiave duplicata nell'oggetto 'dbo.Person'. L'istruzione è stata terminata.

Inoltre ho scritto sul blog più recentemente su una soluzione per applicare un vincolo univoco a due colonne in entrambi gli ordini :

  • Applica un vincolo unico laddove l'ordine non conta