SQL Server - Como inserir um registro e verifique se ele é único
-
06-07-2019 - |
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.
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