2010-03-22 5 views
4

J'ai une base de données SQL Server 2008 dans laquelle j'ai une table pour Tags. Un tag est juste un identifiant et un nom. La définition de la table de balises est la suivante:SQL Question de mise à jour de test simultané

CREATE TABLE [dbo].[Tag](
    [ID] [int] IDENTITY(1,1) NOT NULL, 
    [Name] [varchar](255) NOT NULL 
CONSTRAINT [PK_Tag] PRIMARY KEY CLUSTERED 
(
    [ID] ASC 
)WITH (PAD_INDEX = OFF, 
     STATISTICS_NORECOMPUTE = OFF, 
     IGNORE_DUP_KEY = OFF,  
     ALLOW_ROW_LOCKS = ON, 
     ALLOW_PAGE_LOCKS = ON) 
) 

Le nom est également un index unique. De plus, j'ai plusieurs processus ajoutant des données à cette table à un rythme assez rapide. Ces processus utilisent une procédure stockée qui ressemble à:

ALTER PROC [dbo].[lg_Tag_Insert] 
    @Name varchar(255) 
AS 


DECLARE @ID int 
SET @ID = (select ID from Tag where [email protected]) 

if @ID is null 
    begin 
      INSERT Tag(Name) 
      VALUES (@Name) 

      RETURN SCOPE_IDENTITY() 

    end 
else 
    begin 
     return @ID 
    end 

Mes problèmes est que, autre que d'être un novice à la conception de base de données en même temps, il semble y avoir une condition de course qui me cause de temps en temps obtenir une erreur que je J'essaie d'entrer des clés en double (nom) dans la base de données. L'erreur est:

Impossible d'insérer une ligne de clé en double dans l'objet 'dbo.Tag' avec l'index unique 'IX_Tag_Name'.

Cela a du sens, je ne suis pas sûr de savoir comment résoudre ce problème. Si c'est le code, je saurais comment verrouiller les bonnes zones. SQLServer est une bête assez différente.

Première question est quelle est la bonne façon de coder cette «vérification, puis mise à jour de modèle»? Il semble que je doive avoir un verrou exclusif sur la rangée pendant le contrôle, plutôt qu'un verrou partagé, mais ce n'est pas clair pour moi la meilleure façon de le faire. Toute aide dans la bonne direction sera grandement appréciée. Merci d'avance.

Répondre

0

Le code approprié serait:

  • Dans un SP, en cours d'exécution de préférence avec transaction sérialisable
  • Faire une sélection dans la table des balises pour récupérer un premier identifiant
  • Si null, insérez. L'isolation de transaction s'assurera que les transactions sont sérialisées.

Cache le côté client de sorte que vous n'insérez pas lorsque le client sait déjà qu'il est présent. Les performances seront minimes. Il semble que vous fassiez cela, donc le seul problème peut être votre niveau d'isolation de transaction.

Ce que vous pouvez faire est:

  • Utiliser des connexions séparées pour insérer des balises.
  • Lorsque vous obtenez une erreur en double, ignorez-la, recherchez l'ID, utilisez l'ID. Comme vous êtes sur une connexion séparée, cela n'a pas d'importance.
1

Je préfère les paramètres de sortie (donc je codé de cette façon), mais cela devrait préforme le plus rapide, avec le plus petit nombre hits sur la table:

ALTER PROC [dbo].[lg_Tag_Insert] 
    @Name varchar(255) 
    ,@ID int OUTPUT 
AS 
BEGIN TRY 
    SET @ID=NULL 
    INSERT Tag (Name) VALUES (@Name) 
    SET @ID=SCOPE_IDENTITY() 
END TRY 
BEGIN CATCH 
    SELECT @ID=ID from Tag where [email protected] 
END CATCH 

IF @ID IS NULL 
BEGIN 
    RETURN 1 
END 

RETURN 0 

GO 
0

J'ai trouvé les meilleurs résultats dans les tableaux avec de lourdes insère pour définir la contrainte sur "Ignorer les doublons" et laisse les dupes tomber sur le sol tout en capturant les nouvelles insertions. pour votre sproc vous pouvez éliminer complètement le test et retourner le SCOPE_IDENTITY() ou le null après l'insertion.