レコードが削除されたときに名前の末尾に id 値を追加できます。そのため、誰かが ID 3 を削除すると名前が Thingy3_3 になり、ID 100 を削除すると名前が Thingy3_100 になります。これにより、名前と削除されたフィールドに一意の複合インデックスを作成できますが、名前列を表示するたびにフィルターをかけ、名前の末尾から ID を削除する必要があります。
おそらく、削除された列をDATETIME型のdeleted_at列に置き換えることがより良い解決策になるでしょう。次に、削除されていないレコードのdeleted_atフィールドにnull値があり、nameとdeleted atに一意のインデックスを維持できます。これにより、アクティブな状態で複数の名前を作成することはできなくなりますが、同じ名前を複数回削除することができます。
削除を取り消す前に、同じ名前の行がなく、deleted_at フィールドが null であることを確認するために、レコードの削除を取り消すときに明らかにテストを行う必要があります。
実際には、削除に INSTEAD-OF トリガーを使用して、データベース内にこのすべてのロジックを実装できます。このトリガーはレコードを削除しませんが、代わりに、レコードを削除したときに deleted_at 列を更新します。
次のコード例はこれを示しています
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
このクエリから選択すると、次が返されます
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
したがって、コード内で通常の削除を使用してレコードを削除し、トリガーに詳細を任せることができます。唯一の可能性のある問題 (私が見ることができた) は、既に削除されたレコードを削除すると行が重複する可能性があることでした。そのため、既に削除された行の deleted_at フィールドを更新しないトリガーの条件です。