Maneira mais rápida de determinar se existe um registro

143

Como o título sugere ... Estou tentando descobrir o caminho mais rápido com o mínimo de sobrecarga para determinar se um registro existe em uma tabela ou não.

Consulta de amostra:

SELECT COUNT(*) FROM products WHERE products.id = ?;

    vs

SELECT COUNT(products.id) FROM products WHERE products.id = ?;

    vs

SELECT products.id FROM products WHERE products.id = ?;

Digamos que ?seja trocado por 'TB100'... a primeira e a segunda consultas retornarão exatamente o mesmo resultado (digamos ... 1para esta conversa). A última consulta retornará 'TB100'conforme o esperado ou nada se idnão estiver presente na tabela.

O objetivo é descobrir se o item idestá na tabela ou não. Caso contrário, o programa inserirá o registro em seguida, caso contrário, o programa o ignorará ou executará uma consulta UPDATE com base em outra lógica do programa fora do escopo desta pergunta.

Qual é mais rápido e tem menos despesas gerais? (Isso será repetido dezenas de milhares de vezes por execução do programa e será executado várias vezes ao dia).

(Executando esta consulta no M $ SQL Server a partir de Java por meio do driver JDBC fornecido pelo M $)

SnakeDoc
fonte
1
Isso pode depender do banco de dados. Por exemplo, contar com o Postgres é bastante lento.
Mike Christensen
Desculpe, este é o Java falando com M $ SQL via driver jdbc. Vou atualizar meu OP.
SnakeDoc 7/08/13
2
existe também.
Nikola Markovinović
@Nikola Markovinović: como você o usaria neste caso?
Zerkms 07/08
5
@zerkms Depende do contexto. Se no procedimento armazenado seria if exists(select null from products where id = @id); se em uma consulta chamada diretamente por um cliente select case when exists (...) then 1 else 0 end.
Nikola Markovinović

Respostas:

170

SELECT TOP 1 products.id FROM products WHERE products.id = ?; superará todas as suas sugestões, pois encerrará a execução após encontrar o primeiro registro.

Declan_K
fonte
5
O otimizador não leva isso em consideração ao pesquisar por PK (ou qualquer outra chave exclusiva)?
Zerkms 07/08
3
Ele nunca afirmou que esse era o PK, mas, se sim, o otimizador levaria isso em consideração.
Declan_K
3
@ Declan_K: parece que minha esfera mágica falhou nesse caso e uma coluna intitulada como idnão é PK. Então, marque com +1 seu conselho.
Zerkms
4
Se não for o PK, eu também sugeriria garantir que haja um índice nessa coluna. Caso contrário, a consulta precisará fazer uma varredura da tabela em vez de uma busca mais rápida da tabela.
CD Jorgensen
3
Acho que devemos considerar a resposta @ nenad-zivkovic sobre esta.
Giulio Caccin
192

EXISTS(ou NOT EXISTS) foi projetado especialmente para verificar se algo existe e, portanto, deve ser (e é) a melhor opção. Ele será interrompido na primeira linha que corresponder, para que não exija uma TOPcláusula e, na verdade, não selecione nenhum dado para que não haja sobrecarga no tamanho das colunas. Você pode usar com segurança SELECT *aqui - não é diferente de SELECT 1, SELECT NULLou SELECT AnyColumn... (você pode até usar uma expressão inválida como SELECT 1/0essa e ela não será quebrada) .

IF EXISTS (SELECT * FROM Products WHERE id = ?)
BEGIN
--do what you need if exists
END
ELSE
BEGIN
--do what needs to be done if not
END
Nenad Zivkovic
fonte
isso não precisa primeiro executar a instrução SELECT, depois executar a instrução IF EXISTS ... causando sobrecarga adicional e, portanto, mais tempo de processamento?
SnakeDoc
7
O @SnakeDoc No. Existstrabalha de selecttal maneira que sai assim que uma linha é encontrada. Além disso, existe apenas uma anotação da existência do registro, e não valores reais no registro, economizando a necessidade de carregar a linha do disco (assumindo que os critérios de pesquisa sejam indexados, é claro). Quanto às despesas gerais if- você terá que gastar esse tempo minúsculo de qualquer maneira.
Nikola Markovinović
1
@ NikolaMarkovinović ponto interessante. Não tenho certeza se existe um índice nesse campo e meu SQL iniciante não sabe como descobrir. Estou trabalhando com esse banco de dados em Java via JDBC e o banco de dados está localizado remotamente em um colo em algum lugar. Só recebi um "resumo do banco de dados", que apenas detalha quais campos existem em cada tabela, seu tipo e qualquer FK ou PK. Isso muda alguma coisa?
SnakeDoc 08/08/19
3
@SnakeDoc Para obter informações sobre a estrutura da tabela, incluindo chaves e índices estrangeiros, execute sp_help table_name . Os índices são essenciais quando se trata de recuperar algumas linhas de muitas, quando usando select topou exists; se eles não estiverem presentes, o mecanismo sql terá que executar a verificação da tabela. Esta é a opção de pesquisa de tabela menos desejável. Se você não está autorizado a criar índices, precisará se comunicar com a equipe técnica do outro lado para descobrir se eles os ajustam automaticamente ou se espera que você sugira índices.
Nikola Markovinović
1
@Konstantin Você pode fazer algo assimSELECT CASE WHEN EXISTS(..) THEN 1 ELSE 0 END;
Nenad Zivkovic
21

Nada pode vencer -

SELECT TOP 1 1 FROM products WHERE id = 'some value';

Você não precisa contar para saber se há dados na tabela. E não use alias quando não for necessário.

AgentSQL
fonte
5
Apesar de seu nome idnão é chave primária. Portanto, mesmo que você não esteja contando, ainda precisa encontrar todos os registros correspondentes, possivelmente milhares deles. Sobre o aliasing - o código é um trabalho constante em andamento. Você nunca sabe quando precisará voltar. O alias ajuda a evitar erros estúpidos de tempo de execução; por exemplo, o nome exclusivo da coluna que não precisava de um alias não é mais exclusivo porque alguém criou uma coluna com o mesmo nome em outra tabela unida.
Nikola Markovinović
Sim você está absolutamente certo. O aliasing ajuda muito, mas não acho que faça diferença quando não estiver usando junções. Então, eu disse que não use se não for necessário. :) E você pode encontrar uma longa discussão aqui sobre como verificar a existência. :)
AgentSQL
3
Não sei por que aceitei o termo aliasing. O termo correto é qualifying. Aqui está uma explicação mais longa de Alex Kuznetzov . Sobre consultas de mesa única - é única tabela agora . Porém, mais tarde, quando o bug é descoberto e você está tentando conter o fluxo, o cliente está nervoso, você se junta a outra tabela apenas para enfrentar a mensagem de erro - mensagem facilmente corrigível, mas não nesse momento suado, um pequeno golpe - e você corrige o erro lembrando a nunca deixar uma coluna ...
Nikola Markovinović
1
Não posso ignorar isso agora. Obrigado!! :)
AgentSQL
15
SELECT CASE WHEN EXISTS (SELECT TOP 1 *
                         FROM dbo.[YourTable] 
                         WHERE [YourColumn] = [YourValue]) 
            THEN CAST (1 AS BIT) 
            ELSE CAST (0 AS BIT) END

Essa abordagem retorna um valor booleano para você.

Kris Coleman
fonte
1
Provavelmente, pode omitir a instrução Top e a instrução * para torná-la um pouco mais rápida, pois o Exist sairá assim que encontrar um registro, algo assim: SELECT CASE WHEN EXISTS (SELECT 1 FROM dbo. [YourTable] WHERE [YourColumn] = [YourValue]) THEN CAST (1 AS BIT) ELT CAST (0 AS BIT) END
Stefan Zvonar
Essa sugestão não menciona por que isso seria mais rápido nas instruções internas existentes / não existentes no SQL Server. Sem nenhum benchmarking, seria difícil acreditar que uma declaração de caso produziria um resultado mais rápido do que uma resposta imediata verdadeira / falsa.
Bonez024 09/04/19
8

Você também pode usar

 If EXISTS (SELECT 1 FROM dbo.T1 WHERE T1.Name='Scot')
    BEGIN
         --<Do something>
    END 

ELSE    
     BEGIN
       --<Do something>
     END
atik sarker
fonte
7

Não pense que alguém já o tenha mencionado, mas se tiver certeza de que os dados não serão alterados, você também pode aplicar a dica NoLock para garantir que não seja bloqueada durante a leitura.

SELECT CASE WHEN EXISTS (SELECT 1 
                     FROM dbo.[YourTable] WITH (NOLOCK)
                     WHERE [YourColumn] = [YourValue]) 
        THEN CAST (1 AS BIT) 
        ELSE CAST (0 AS BIT) END
Stefan Zvonar
fonte
3
SELECT COUNT(*) FROM products WHERE products.id = ?;

Esta é a solução de banco de dados relacional cruzado que funciona em todos os bancos de dados.

rapaz desonesto
fonte
6
No entanto, você forçar o db de varrer todos os registros, muito lento em grandes mesas
amd
@amd cuidado para explicar o porquê?
UmNyobe
@ amd seu comentário faz todo sentido. Esta consulta é mais um FIND ALL do que FIND ANY.
UmNyobe
1

Abaixo está a maneira mais simples e rápida de determinar se um registro existe ou não no banco de dados. Ainda bem que funciona em todos os bancos de dados relacionais

SELECT distinct 1 products.id FROM products WHERE products.id = ?;
Manas Prasad
fonte
0
create or replace procedure ex(j in number) as
i number;
begin
select id into i from student where id=j;
if i is not null then
dbms_output.put_line('exists');
end if;
exception
   when no_data_found then
        dbms_output.put_line(i||' does not exists');

end;
Kiran
fonte
2
Possivelmente, seu código funciona muito bem, mas seria melhor se você adicionar algumas informações adicionais para que seja melhor compreensível.
Idmean
0

Eu usei isso no passado e não requer uma verificação completa da tabela para ver se existe algo. É super rápido ...

UPDATE TableName SET column=value WHERE column=value
IF @@ROWCOUNT=0
BEGIN
     --Do work
END             
Eric Parsons
fonte
0

Para aqueles que se deparam com isso a partir do MySQL ou do Oracle background - o MySQL suporta a cláusula LIMIT para selecionar um número limitado de registros, enquanto o Oracle usa o ROWNUM.

Werner
fonte