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

Cosa fare quando desidero utilizzare i vincoli del database ma contrassegnare solo come eliminato anziché eliminare?

Puoi aggiungere il valore id alla fine del nome quando un record viene eliminato, quindi quando qualcuno elimina l'id 3 il nome diventa Thingy3_3 e quindi quando elimina l'id 100 il nome diventa Thingy3_100. Ciò ti consentirebbe di creare un indice composito univoco sul nome e sui campi eliminati, ma devi filtrare la colonna del nome ogni volta che la visualizzi e rimuovere l'id dalla fine del nome.

Forse una soluzione migliore sarebbe quella di sostituire la colonna eliminata con una colonna cancellata_at di tipo DATETIME. È quindi possibile mantenere un indice univoco su nome ed eliminato in, con un record non eliminato avente un valore nullo nel campo eliminato_at. Ciò impedirebbe la creazione di più nomi in uno stato attivo, ma ti consentirebbe di eliminare lo stesso nome più volte.

Ovviamente è necessario eseguire un test durante l'annullamento dell'eliminazione di un record per assicurarsi che non ci siano righe con lo stesso nome e un campo null_at eliminato prima di consentire l'annullamento dell'eliminazione.

È possibile implementare effettivamente tutta questa logica all'interno del database utilizzando un trigger INSTEAD-OF per l'eliminazione. Questo attivatore non eliminerebbe i record, ma aggiornerebbe invece la colonna delete_at quando elimini un record.

Il codice di esempio seguente lo dimostra

CREATE TABLE swtest (  
    id          INT IDENTITY,  
    name        NVARCHAR(20),  
    deleted_at  DATETIME  
)  
GO  
CREATE TRIGGER tr_swtest_delete ON swtest  
INSTEAD OF DELETE  
AS  
BEGIN  
    UPDATE swtest SET deleted_at = getDate()  
    WHERE id IN (SELECT deleted.id FROM deleted)
    AND deleted_at IS NULL      -- Required to prevent duplicates when deleting already deleted records  
END  
GO  

CREATE UNIQUE INDEX ix_swtest1 ON swtest(name, deleted_at)  

INSERT INTO swtest (name) VALUES ('Thingy1')  
INSERT INTO swtest (name) VALUES ('Thingy2')  
DELETE FROM swtest WHERE id = SCOPE_IDENTITY()  
INSERT INTO swtest (name) VALUES ('Thingy2')  
DELETE FROM swtest WHERE id = SCOPE_IDENTITY()  
INSERT INTO swtest (name) VALUES ('Thingy2')  

SELECT * FROM swtest  
DROP TABLE swtest  

La selezione da questa query restituisce quanto segue

id      name       deleted_at
1       Thingy1    NULL
2       Thingy2    2009-04-21 08:55:38.180
3       Thingy2    2009-04-21 08:55:38.307
4       Thingy2    NULL

Quindi all'interno del tuo codice puoi eliminare i record utilizzando una normale eliminazione e lasciare che il trigger si occupi dei dettagli. L'unico problema possibile (che ho potuto vedere) era che l'eliminazione di record già eliminati poteva comportare righe duplicate, da qui la condizione nel trigger di non aggiornare il campo eliminato_at su una riga già eliminata.