Pregunta

Estoy tratando de encontrar la mejor manera de insertar un registro en una sola tabla, pero solo si el elemento aún no existe. La CLAVE en este caso es un campo NVARCHAR (400). Para este ejemplo, imaginemos que es el nombre de una palabra en el Oxford English Dictionary / inserte aquí su diccionario favorito. Además, supongo que tendré que hacer que el campo de Word sea una clave principal. (la tabla también tendrá un identificador único PK también).

Entonces ... podría obtener estas palabras que necesito agregar a la tabla ...

ej.

  • Gato
  • Perro
  • Foo
  • Bar
  • PewPew
  • etc ...

Así que tradicionalmente, intentaría lo siguiente (pseudocódigo)

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

es decir. Si la palabra no existe, insértela.

Ahora ... el problema que me preocupa es que estamos recibiendo MUCHOS éxitos ... así que es posible que la palabra pueda insertarse desde otro proceso entre SELECT y INSERT ... que luego arrojaría un error de restricción? (es decir, una condición de carrera ).

Entonces pensé que podría hacer lo siguiente ...

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

básicamente, inserta una palabra cuando no existe.

Dejando a un lado la sintaxis incorrecta, no estoy seguro de si esto es malo o bueno debido a cómo bloquea la tabla (si lo hace) y no es tan eficaz en una tabla que obtiene lecturas masivas y muchas escrituras.

Entonces, ¿qué piensan / hacen los gurús de SQL?

Esperaba tener una inserción simple y 'atrapar' eso por cualquier error arrojado.

¿Fue útil?

Solución

Su solución:

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

... es tan bueno como parece. Podría simplificarlo a esto:

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

... porque EXISTS en realidad no necesita devolver ningún registro, por lo que el optimizador de consultas no se molestará en mirar qué campos solicitó.

Sin embargo, como mencionas, esto no es particularmente eficaz, ya que bloqueará toda la mesa durante el INSERT. Excepto que, si agrega un índice único (no necesita ser la clave principal) a Word, solo tendrá que bloquear las páginas relevantes.

Su mejor opción es simular la carga esperada y observar el rendimiento con SQL Server Profiler. Como con cualquier otro campo, la optimización prematura es algo malo. Defina métricas de rendimiento aceptables y luego mida antes de hacer cualquier otra cosa.

Si eso todavía no le brinda un rendimiento adecuado, entonces hay un montón de técnicas del campo de almacenamiento de datos que podrían ayudar.

Otros consejos

Creo que he encontrado una respuesta mejor (o al menos más rápida) a esto. Cree un índice como:

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]

Incluya todas las columnas que definen la unicidad. La parte importante es IGNORE_DUP_KEY = ON. Eso convierte inserciones no únicas en advertencias. SSIS ignora estas advertencias y aún puede usar fastload también.

Si está utilizando MS SQL Server, puede crear un índice único en las columnas de su tabla que debe ser único (documentado aquí ):

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

Especifique Clustered o NonClustered , según su caso. Además, si desea ordenarlo (para permitir una búsqueda más rápida), especifique ASC o DESC para el orden de clasificación.

Consulte aquí , si desea obtener más información sobre la arquitectura de índices.

De lo contrario, podría usar RESTRICCIONES ÚNICAS como documentado aquí :

ALTER TABLE Words
ADD CONSTRAINT UniqueWord
UNIQUE (Word); 

Tuve un problema similar y así es como lo resolví

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
    ) 

mientras que la restricción única es sin duda un camino a seguir, también puede usar esto para su lógica de inserción: http://www.sqlteam.com/ artículo / application-locks-or-mutexes-in-sql-server-2005

básicamente no colocas ningún candado en la tabla a continuación, por lo tanto, no te preocupes por las lecturas mientras que sus controles de existencia se realizarán bien.

es un mutex en código sql.

No puedo hablar de los detalles de MS SQL, pero un punto de una clave principal en SQL es garantizar la unicidad. Entonces, por definición en términos genéricos de SQL, una clave primaria es uno o más campos que son exclusivos de una tabla. Si bien hay diferentes formas de aplicar este comportamiento (reemplazar la entrada anterior por la nueva versus rechazar la nueva), me sorprendería que MS SQL no tuviera un mecanismo para hacer cumplir este comportamiento y que no fuera así. rechazar la nueva entrada. Solo asegúrese de establecer la clave principal en el campo de Word y debería funcionar.

Sin embargo, una vez más, niego que todo esto sea por mi conocimiento de la programación MySQL y mi clase de bases de datos, así que disculpas si estoy fuera de las complejidades 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
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top