SQL: Como verificar corretamente se existe um registro

207

Ao ler alguma documentação relacionada ao SQL Tuning, encontrei o seguinte:

SELECT COUNT(*) :

  • Conta o número de linhas.
  • Muitas vezes, é usado incorretamente para verificar a existência de um registro.

É SELECT COUNT(*)realmente tão ruim assim?

Qual é a maneira correta de verificar a existência de um registro?

systempuntoout
fonte

Respostas:

252

É melhor usar um dos seguintes:

-- Method 1.
SELECT 1
FROM table_name
WHERE unique_key = value;

-- Method 2.
SELECT COUNT(1)
FROM table_name
WHERE unique_key = value;

A primeira alternativa não deve fornecer nenhum resultado ou um resultado, a segunda contagem deve ser zero ou um.

Qual a idade da documentação que você está usando? Embora você tenha lido bons conselhos, a maioria dos otimizadores de consulta nos RDBMS recentes é otimizada de SELECT COUNT(*)qualquer maneira; portanto, embora exista uma diferença na teoria (e bancos de dados mais antigos), você não deve notar nenhuma diferença na prática.

Martin Schapendonk
fonte
1
Esclarecerei que pretendi "chave única" com a cláusula "chave = valor", mas, além disso, ainda estou por trás da minha resposta.
Martin Schapendonk
1
ESTÁ BEM. Com essa premissa, de fato, a consulta retornaria apenas um ou zero registro. MAS: A pergunta não se limita a uma coluna exclusiva. Além disso: a segunda contagem de consulta (1) é equivalente à contagem (*) de um ponto de vista prático.
Martin Ba
1
A pergunta diz "qual é a maneira correta de verificar a existência de um registro". Eu interpretei isso como singular, como em: 1 registro. A diferença entre count (*) e count (1) já está coberta pela minha resposta. Prefiro count (1) porque ele não depende de uma implementação específica do RDBMS.
Martin Schapendonk
192

Eu preferiria não usar a função Count:

IF [NOT] EXISTS ( SELECT 1 FROM MyTable WHERE ... )
     <do smth>

Por exemplo, se você deseja verificar se o usuário existe antes de inseri-lo no banco de dados, a consulta pode ser assim:

IF NOT EXISTS ( SELECT 1 FROM Users WHERE FirstName = 'John' AND LastName = 'Smith' )
BEGIN
    INSERT INTO Users (FirstName, LastName) VALUES ('John', 'Smith')
END
Pavel Morshenyuk
fonte
Geralmente, usamos (a verificação) quando queremos fazer algo, então sua resposta é mais completa.
Abner Escócio 7/11
É bom mencionar que, usando o T-SQL
Bronek 18/01/19
20

Você pode usar:

SELECT 1 FROM MyTable WHERE <MyCondition>

Se não houver registro correspondente à condição, o conjunto de registros resultante estará vazio.

Cătălin Pitiș
fonte
Você quis dizer TOP 1? -> (SELECT TOP 1 FROM MyTable WHERE <MyCondition>)
Jacob
6
Não, eu quis dizer exatamente "1"
Cătălin Pitiș
1
para habilitar o otimizador de consulta a saber que você não vai ler / precisar dos conjuntos de dados restantes, você deve indicar SELECT TOP 1 1 FROM ... WHERE ... (ou usar as dicas de consulta apropriadas para seu RDBS)
eFloh
3
O próprio operador Exists tenta recuperar apenas o mínimo absoluto de informações; portanto, a adição do TOP 1 não faz nada, exceto adicionar 5 caracteres ao tamanho da consulta. - sqlservercentral.com/blogs/sqlinthewild/2011/04/05/…
AquaAlex
13

As outras respostas são bastante boas, mas também seria útil adicionar LIMIT 1(ou equivalente) para impedir a verificação de linhas desnecessárias.

JesseW
fonte
3
Se qualquer consulta "verificar a existência" retornar mais de uma linha, acho mais útil checar sua cláusula WHERE em vez de LIMITAR o número de resultados.
Martin Schapendonk
2
Acho limite é utilizado no Oracle e não no SQL Server
Shantanu Gupta
7
Estou considerando o caso em que eles podem ser legitimamente várias linhas - onde a pergunta é: "Existem (uma ou mais) linhas que satisfazem essa condição?" Nesse caso, você não deseja olhar para todos eles, apenas um.
JesseW
1
@ Shantanu - Eu sei, é por isso que vinculei ao artigo (muito completo) da en.wikipedia que explica as outras formas.
JesseW
11
SELECT COUNT(1) FROM MyTable WHERE ...

irá percorrer todos os registros. Esta é a razão pela qual é ruim usar a existência de registros.

eu usaria

SELECT TOP 1 * FROM MyTable WHERE ...

Depois de encontrar 1 registro, ele encerrará o loop.

oski
fonte
No caso de SELECT TOP 1ele realmente terminará depois de encontrar um ou continua a encontrar tudo para poder dizer qual é o TOP?
Eirik H
3
PS: Para ter certeza que eu sempreIF EXISTS (SELECT TOP 1 1 FROM ... WHERE ..)
Eirik H
o operador Star forçará o DBMS a acessar o índice em cluster em vez de apenas o (s) índice (s) necessário (s) para sua condição de associação. portanto, é melhor usar um valor constante como resultado, ou seja, selecione 1 1 .... Isso retornará 1 ou DB-Null, dependendo da condição de uma correspondência ou não.
eFloh
é legal. Eu gosto da primeira.
Istaker
10

Você pode usar:

SELECT COUNT(1) FROM MyTable WHERE ... 

ou

WHERE [NOT] EXISTS 
( SELECT 1 FROM MyTable WHERE ... )

Isso será mais eficiente do que SELECT * que se você simplesmente selecionar o valor 1 para cada linha, em vez de todos os campos.

Há também uma diferença sutil entre COUNT (*) e COUNT (nome da coluna):

  • COUNT(*) contará todas as linhas, incluindo nulos
  • COUNT(column name)contará apenas ocorrências não nulas do nome da coluna
Winston Smith
fonte
2
Você está assumindo erroneamente que um DBMS verificará de alguma forma todas essas colunas. A diferença de desempenho entre count(1)e count(*)será diferente apenas no DBMS com morte cerebral.
paxdiablo
2
Não, estou dizendo que você está realmente confiando nos detalhes da implementação quando afirma que será mais eficiente. Se você realmente deseja garantir o melhor desempenho, deve traçar um perfil para a implementação específica usando dados representativos ou simplesmente esquecê-lo totalmente. Qualquer outra coisa é potencialmente enganosa e pode mudar drasticamente ao passar (por exemplo) do DB2 para o MySQL.
paxdiablo
1
Quero deixar claro que não estou ofendendo sua resposta. Isso é útil. O único problema com o qual discordo é a reivindicação de eficiência, já que fizemos avaliações no DB2 / z e descobrimos que não há diferença real entre count(*)e count(1). Se é esse o caso de outros DBMS ', não posso dizer.
precisa
3
"Qualquer outra coisa é potencialmente enganosa e pode mudar drasticamente ao passar (por exemplo) do DB2 para o MySQL". É muito mais provável que você seja mordido pela degradação do desempenho de SELECT COUNT (*) ao mover DBMS do que por uma diferença de implementação no SELECT 1. ou COUNT (1). Acredito firmemente na escrita do código que expressa mais claramente exatamente o que você deseja alcançar, em vez de depender de otimizadores ou compiladores para padronizar o comportamento desejado.
Winston Smith
1
Declaração enganosa "COUNT (*)" significa 'contar as linhas' ponto final. Não requer acesso a nenhuma coluna específica. E, na maioria dos casos, nem exigirá acesso à própria linha, pois qualquer índice exclusivo é suficiente.
James Anderson
9

Você pode usar:

SELECT 1 FROM MyTable WHERE... LIMIT 1

Usar select 1 para impedir a verificação de campos desnecessários.

Use LIMIT 1 para impedir a verificação de linhas desnecessárias.

user3059943
fonte
3
Bom ponto, mas Limite trabalha em MySQL e PostgreSQL, principais obras sobre SQL Server, você deve observar que em sua resposta
Leo Gurdian
0

Estou usando desta maneira:

IIF(EXISTS (SELECT TOP 1 1 
                FROM Users 
                WHERE FirstName = 'John'), 1, 0) AS DoesJohnExist
DiPix
fonte
0

Outra opção:

SELECT CASE
    WHEN EXISTS (
        SELECT 1
        FROM [MyTable] AS [MyRecord])
    THEN CAST(1 AS BIT) ELSE CAST(0 AS BIT)
END
Pranav
fonte