Pergunta

Eu estou tentando descobrir a melhor maneira de inserir um registro em uma única tabela, mas somente se o item já não existe. A chave neste caso é um campo NVARCHAR (400). Para este exemplo, vamos fingir que é o nome de um palavra no Dicionário Oxford de Inglês / inserir seu dicionário fav aqui. Além disso, eu estou supondo que vou precisar para tornar o campo Palavra uma chave primária. (A tabela também terá um PK identificador único também).

Então .. i pode obter estas palavras que eu preciso para adicionar à mesa ...

por exemplo.

  • Cat
  • Dog
  • Foo
  • Bar
  • PewPew
  • etc ...

Assim, tradicionalmente, gostaria de tentar o seguinte (código pseudo)

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

ie. Se a palavra não existe, em seguida, insira-o.

Agora .. O problema que eu estou preocupado é que estamos recebendo muitos hits .. por isso é possível que a palavra pode ser inserido de outro processo entre o SELECT e INSERT .. que passaria então a jogar um erro de restrição? (Ie. A Condição de corrida ).

Então eu pensei que eu poderia ser capaz de fazer o seguinte ...

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

Basicamente, Inserir uma palavra quando ela não existe.

Bad sintaxe de lado, eu não tenho certeza se isso é bom ou ruim por causa de como ele bloqueia a tabela (se isso acontecer), e não é que alto desempenho em uma tabela que ele ficar enorme lê e abundância de gravações.

Assim - O que você gurus Sql pensar / fazer

Eu estava esperando para ter uma inserção simples e 'pegar' que por quaisquer erros lançada.

Foi útil?

Solução

A sua solução:

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

... é quase tão bom quanto ele ganha. Você poderia simplificá-lo para isto:

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

... porque não existe realmente não precisa retornar todos os registros, de modo que o otimizador de consulta não vai incomodar a olhar para os campos que pediu.

Como você menciona, no entanto, este não é particularmente alto desempenho, porque ele vai bloquear toda a tabela durante o INSERT. Só que, se você adicionar um índice exclusivo (não precisa ser a chave primária) para Word, em seguida, ele vai só precisa bloquear as páginas relevantes.

Sua melhor opção é para simular a carga esperada e olhar para o desempenho com o SQL Server Profiler. Tal como acontece com qualquer outro campo, otimização prematura é uma coisa ruim. Definir métricas de desempenho aceitáveis ??e, em seguida, medir antes de fazer qualquer outra coisa.

Se isso ainda não é o que lhe dá um desempenho adequado, então há um monte de técnicas a partir dos dados de campo de armazenagem que podem ajudar.

Outras dicas

Eu acho que eu encontrei a melhor (ou pelo menos mais rápido) resposta para isso. Criar um í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]

Incluir todas as colunas que definem singularidade. A parte importante é IGNORE_DUP_KEY = ON. Que transforma inserções não exclusivas sobre advertências. SSIS ignora esses avisos e você ainda pode usar fastload também.

Se você estiver usando o MS SQL Server, você pode criar um índice exclusivo em colunas de sua tabela que precisa ser exclusivo (documentado aqui ):

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

Especifique Clustered ou NonClustered, dependendo do seu caso. Além disso, se você quer que ele classificado (para permitir mais rápido buscando), especifique ASC ou DESC para a ordem de classificação.

Consulte aqui , se você quiser saber mais sobre índices arquitetura.

Caso contrário, você poderia usar UNIQUE CONSTRAINTS como documentado aqui :

ALTER TABLE Words
ADD CONSTRAINT UniqueWord
UNIQUE (Word); 

Eu tive problema semelhante e é assim que eu resolver isso

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
    ) 

enquanto restrição exclusiva é uma maneira certaily para ir, você também pode usar isso para sua lógica de inserção: http://www.sqlteam.com/ article / aplicação de portas-ou-mutexes-em-sql-server-2005

basicamente você não colocar quaisquer bloqueios na tabela abaixo, portanto, não se preocupar com a lê enquanto seus cheques existência será realizada ok.

é um mutex no código SQL.

Eu não posso falar com as indicações do MS SQL, mas um ponto de uma chave primária no SQL é para garantir a exclusividade. Então, por definição em termos SQL genéricos, uma chave primária é um ou mais campos que é exclusivo para uma tabela. Embora existam diferentes maneiras de fazer cumprir este comportamento (substitua a entrada antiga com o novo vs. rejeitar o novo) Eu ficaria surpreso se o MS SQL ambos não têm um mecanismo para impor esse comportamento e que não era para rejeitar a nova entrada. Apenas certifique-se de definir a chave primária para o campo do Word e deve trabalho.

Mais uma vez, porém, eu assumem tudo isso é do meu conhecimento de programação MySQL e minha aula de bancos de dados, então desculpas se eu estou fora sobre os meandros da 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 em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top