Cette déclaration:
INSERT INTO deleteme
SELECT #t.id, 'test' FROM #t
LEFT JOIN deleteme ON deleteme.id = #t.id
WHERE deleteme.id IS NULL;
... échouera avec une violation de clé primaire dans le scénario simultané (c'est-à-dire lors de l'insertion de la même clé qui est absente dans deleteme de plusieurs threads en même temps) avec le niveau d'isolement de transaction par défaut de READ COMMITTED
:
Erreur: violation de la contrainte PRIMARY KEY 'PK_DeleteMe'. Impossible d'insérer une clé en double dans l'objet 'dbo.deleteme'.
Quelle est la meilleure façon d'empêcher cela?
Le tableau ressemble à ceci:
CREATE TABLE [dbo].[DeleteMe](
[id] [uniqueidentifier] NOT NULL,
[Value] [varchar](max) NULL,
CONSTRAINT [PK_DeleteMe] PRIMARY KEY ([id] ASC));
Mise à jour
Des commentaires:
Pourquoi avez-vous plusieurs sessions, qui peuvent évidemment tirer le même identifiant de quelque part, en utilisant la même table permanente sans avoir une sorte de clé de session pour les distinguer? Même sans erreur en double, comment allez-vous savoir quelles lignes appartiennent à quelle session?
Il s'agit d'une procédure stockée appelée par un service externe qui remplit les données de cette table. Le service génère l'ID d'enregistrement et il ne garantit pas qu'il n'enverra pas les mêmes données deux fois. Ou envoyez les mêmes données en même temps.
Le code de la base de données est censé supprimer tous les enregistrements avec le même identifiant, s'il en existe déjà un. Le service n'envoie jamais deux enregistrements avec le même identifiant dans le même lot.
Si vous avez vraiment besoin d'exécuter plusieurs threads en même temps, vous pouvez activer le ignore_dup_key
option sur la clé primaire.
Cela donnera simplement un avertissement au lieu d'une erreur lorsqu'une insertion entraînera une violation de clé en double. Mais au lieu d'échouer, il supprimera les lignes qui, si elles étaient insérées, entraîneraient la violation de l'unicité.
CREATE TABLE [dbo].[DeleteMe](
[id] [uniqueidentifier] NOT NULL,
[Value] [varchar](max) NULL,
CONSTRAINT [PK_DeleteMe]
PRIMARY KEY ([id] ASC)
WITH (IGNORE_DUP_KEY = ON));
Explication détaillée de Paul White sur l'option IGNORE_DUP_KEY . Merci Paul.
Le nom de la table DeleteMe
suggère que vous accumulez des ID dans cette table, puis supprimez périodiquement des lignes avec ces ID d'une autre table permanente. Est-ce vrai?
Si c'est vrai, vous pouvez autoriser DeleteMe
à avoir des ID en double. Ayez simplement un index non unique sur id
au lieu d'unique (et puisque id
est uniqueidentifier
, il est logique de rendre cet index non cluster également):
CREATE TABLE [dbo].[DeleteMe](
[id] [uniqueidentifier] NOT NULL,
[Value] [varchar](max) NULL,
)
CREATE NONCLUSTERED INDEX [IX_ID] ON [dbo].[DeleteMe]
(
[id] ASC
)
Si vous utilisez DeleteMe
pour supprimer des lignes d'un autre MainTable
comme celui-ci, alors peu importe si id
dans DeleteMe
est unique ou non:
DELETE FROM MainTable
WHERE MainTable.ID IN (SELECT id FROM DeleteMe)
BTW, la requête pour remplir DeleteMe
devient également beaucoup plus simple:
INSERT INTO DeleteMe (id, Value)
SELECT #t.id, 'test'
FROM #t