Question

J'essaie de trouver le meilleur moyen d'insérer un enregistrement dans une seule table, mais uniquement si l'élément n'existe pas déjà. La clé dans ce cas est un champ NVARCHAR (400). Pour cet exemple, supposons qu'il s'agisse du nom d'un mot dans le dictionnaire anglais Oxford / insérez votre dictionnaire préféré ici. En outre, je suppose que je devrai faire du champ Word une clé primaire. (la table aura également un identifiant unique PK également).

Donc .. je pourrais avoir ces mots que je dois ajouter à la table ...

par exemple.

  • chat
  • Chien
  • Foo
  • Bar
  • PewPew
  • etc ...

Traditionnellement, j'essaierais ce qui suit (pseudo-code)

SELECT WordID FROM Words WHERE Word = @Word
IF WordID IS NULL OR WordID <= 0
    INSERT INTO Words VALUES (@Word)

c'est à dire. Si le mot n'existe pas, insérez-le.

Maintenant… le problème qui me préoccupe est que nous obtenons BEAUCOUP de hits .. Il est donc possible que le mot soit inséré à partir d'un autre processus entre SELECT et INSERT .. qui jetterait alors une erreur de contrainte? (c'est-à-dire une une situation de concurrence ).

J'ai alors pensé que je pourrais peut-être faire ce qui suit ...

INSERT INTO Words (Word)
SELECT @Word
WHERE NOT EXISTS (SELECT WordID FROM Words WHERE Word = @Word)

fondamentalement, insérez un mot quand il n’existe pas.

Mauvaise syntaxe mise à part, je ne suis pas sûr que ce soit bon ou mauvais à cause de la façon dont il verrouille la table (si c'est le cas) et n'est pas aussi performant sur une table qu'il obtient des lectures massives et beaucoup d'écritures.

Alors, qu'en pensez-vous, gourous de la SQL?

J'espérais avoir un simple insert et «rattraper» celui des erreurs renvoyées.

Était-ce utile?

La solution

Votre solution:

INSERT INTO Words (Word)
    SELECT @Word
WHERE NOT EXISTS (SELECT WordID FROM Words WHERE Word = @Word)

... est à peu près aussi bon qu'il obtient. Vous pouvez le simplifier comme suit:

INSERT INTO Words (Word)
    SELECT @Word
WHERE NOT EXISTS (SELECT * FROM Words WHERE Word = @Word)

... car EXISTS n'a pas besoin de renvoyer d'enregistrements. L'optimiseur de requêtes ne se soucie plus des champs que vous avez demandés.

Comme vous l'avez dit, ce n'est pas particulièrement performant, car il verrouille toute la table pendant l'insertion. Sauf que, si vous ajoutez un index unique (il n'est pas nécessaire que ce soit la clé primaire) à Word, il vous suffira de verrouiller les pages correspondantes.

Votre meilleure option consiste à simuler la charge attendue et à examiner les performances avec SQL Server Profiler. Comme dans tout autre domaine, une optimisation prématurée est une mauvaise chose. Définissez des mesures de performance acceptables, puis mesurez avant de faire quoi que ce soit d'autre.

Si cela ne vous permet toujours pas d'obtenir des performances suffisantes, de nombreuses techniques du domaine de l'entreposage de données pourraient vous aider.

Autres conseils

Je pense avoir trouvé une réponse meilleure (ou au moins plus rapide) à cette question. Créez un index tel que:

CREATE UNIQUE NONCLUSTERED INDEX [IndexTableUniqueRows] ON [dbo].[table] 
(
    [Col1] ASC,
    [Col2] ASC,

)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = ON, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]

Incluez toutes les colonnes qui définissent l'unicité. La partie importante est IGNORE_DUP_KEY = ON. Cela transforme des insertions non uniques en avertissements. SSIS ignore ces avertissements et vous pouvez toujours utiliser le chargement rapide.

Si vous utilisez MS SQL Server, vous pouvez créer un index unique sur les colonnes de votre table (documenté ici ):

CREATE UNIQUE [ CLUSTERED | NONCLUSTERED ] INDEX <index_name>
    ON Words ( word [ ASC | DESC ])

Spécifiez en cluster ou non en cluster , selon votre cas. De même, si vous souhaitez le trier (pour permettre une recherche plus rapide), spécifiez ASC ou DESC pour l'ordre de tri.

Voir ici , si vous souhaitez en savoir plus sur l'architecture des index.

Sinon, vous pourriez utiliser UNIQUE CONSTRAINTS comme documenté ici :

ALTER TABLE Words
ADD CONSTRAINT UniqueWord
UNIQUE (Word); 

J'ai eu un problème similaire et voici comment je l'ai résolu

insert into Words
( selectWord , Fixword)
SELECT word,'theFixword'
FROM   OldWordsTable
WHERE 
(
    (word LIKE 'junk%') OR
     (word LIKE 'orSomthing') 

)
and word not in 
    (
        SELECT selectWord FROM words WHERE selectWord = word
    ) 

Alors que la contrainte unique est certainement une façon de faire, vous pouvez également l’utiliser pour votre logique d’insertion: http://www.sqlteam.com/ article / application-locks-ou-mutexes-in-sql-server-2005

En gros, vous ne mettez pas de verrou sur la table ci-dessous, vous vous inquiétez donc pas des lectures. alors que vos contrôles d’existence seront effectués correctement.

c'est un mutex en code SQL.

Je ne peux pas parler des détails de MS SQL, mais l'un des points d'une clé primaire en SQL est de garantir l'unicité. Donc, par définition, en termes génériques SQL, une clé primaire est un ou plusieurs champs uniques à une table. Bien qu'il existe différentes manières d'appliquer ce comportement (remplacer l'ancienne entrée par la nouvelle par opposition à la nouvelle), je serais surpris de constater que MS SQL ne dispose pas d'un mécanisme pour appliquer ce comportement et qu'il ne le ferait pas. rejette la nouvelle entrée. Assurez-vous simplement que vous définissez la clé primaire sur le champ Word et que cela devrait fonctionner.

Encore une fois, je nie que tout cela est dû à ma connaissance de la programmation MySQL et de ma classe de bases de données, alors excuses-moi si je me laisse aller à la complexité de MS SQL.

declare @Error int

begin transaction
  INSERT INTO Words (Word) values(@word)
  set @Error = @@ERROR
  if @Error <> 0 --if error is raised
  begin
      goto LogError
  end
commit transaction
goto ProcEnd

LogError:
rollback transaction
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top