SQL: Cómo verificar adecuadamente si existe un registro

207

Mientras leía documentación relacionada con SQL Tuning, encontré esto:

SELECT COUNT(*) :

  • Cuenta el número de filas.
  • A menudo se usa incorrectamente para verificar la existencia de un registro.

¿Es SELECT COUNT(*)realmente tan malo?

¿Cuál es la forma correcta de verificar la existencia de un registro?

systemmpuntoout
fuente

Respuestas:

255

Es mejor usar cualquiera de los siguientes:

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

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

La primera alternativa no debería darle ningún resultado o un resultado, el segundo conteo debería ser cero o uno.

¿Cuántos años tiene la documentación que está utilizando? Aunque ha leído buenos consejos, la mayoría de los optimizadores de consultas en RDBMS recientes optimizan de SELECT COUNT(*)todos modos, por lo que si bien hay una diferencia en teoría (y bases de datos más antiguas), no debería notar ninguna diferencia en la práctica.

Martin Schapendonk
fuente
1
Aclararé que tenía la intención de "clave única" con la cláusula "clave = valor", pero aparte de eso, todavía estoy detrás de mi respuesta.
Martin Schapendonk
1
OKAY. Con esa premisa, la consulta devolvería solo uno o cero registros. PERO: La pregunta no se limita a una columna única. Además: el segundo recuento de consultas (1) es equivalente al recuento (*) de un POV práctico.
Martin Ba
1
La pregunta dice "¿cuál es la forma correcta de verificar la existencia de un registro A". Interpreté eso como singular, como en: 1 registro. La diferencia entre count (*) y count (1) ya está cubierta por mi respuesta. Prefiero count (1) porque no depende de una implementación específica de RDBMS.
Martin Schapendonk
192

Preferiría no usar la función Count en absoluto:

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

Por ejemplo, si desea verificar si el usuario existe antes de insertarlo en la base de datos, la consulta puede verse así:

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
fuente
Generalmente lo usamos (la verificación) cuando queremos hacer algo, entonces su respuesta es más completa.
Abner Escócio
Es bueno mencionar que al usar T-SQL
Bronek
20

Puedes usar:

SELECT 1 FROM MyTable WHERE <MyCondition>

Si no hay ningún registro que coincida con la condición, el conjunto de registros resultante está vacío.

Cătălin Pitiș
fuente
¿Quiso decir TOP 1? -> (SELECCIONE EL TOP 1 DE MyTable WHERE <MyCondition>)
Jacob
66
No, quise decir exactamente "1"
Cătălin Pitiș
1
para permitir que el optimizador de consultas sepa incluso que no leerá / no necesitará los conjuntos de datos restantes, debe indicar SELECCIONAR TOP 1 1 DESDE ... DONDE ... (o usar las sugerencias de consulta apropiadas para su RDBS)
eFloh
3
El operador Exists mismo intenta recuperar solo el mínimo absoluto de información, por lo que la adición de TOP 1 no hace nada excepto agregar 5 caracteres al tamaño de la consulta. - sqlservercentral.com/blogs/sqlinthewild/2011/04/05/…
AquaAlex
13

Las otras respuestas son bastante buenas, pero también sería útil agregar LIMIT 1(o el equivalente , para evitar la verificación de filas innecesarias.

JesseW
fuente
3
Si alguna consulta de "verificación de existencia" devuelve más de una fila, creo que es más útil verificar su cláusula WHERE en lugar de LIMITAR el número de resultados.
Martin Schapendonk
2
Creo que Limit se usa en Oracle y no en SQL Server
Shantanu Gupta
77
Estoy considerando el caso en el que legítimamente pueden ser múltiples filas, donde la pregunta es: "¿Hay (una o más) filas que satisfagan esta condición?" En ese caso, no querrás mirarlos a todos, solo a uno.
JesseW
1
@Shantanu - Lo sé, es por eso que me vinculé al artículo (muy completo) en.wikipedia que explica las otras formas.
JesseW el
11
SELECT COUNT(1) FROM MyTable WHERE ...

recorrerá todos los registros. Esta es la razón por la que es malo usarlo para la existencia de registros.

yo usaría

SELECT TOP 1 * FROM MyTable WHERE ...

Después de encontrar 1 registro, terminará el ciclo.

oski
fuente
¿En caso de SELECT TOP 1que realmente termine después de encontrar uno o continúa encontrando todos para poder decir cuál es TOP?
Eirik H
3
PD: Para estar seguro de que siempreIF EXISTS (SELECT TOP 1 1 FROM ... WHERE ..)
Eirik H
el operador Star forzará al DBMS a acceder al índice agrupado en lugar de solo los índices que se necesitarán para su condición de unión. por lo tanto, es mejor usar un valor constante como resultado, es decir, seleccionar top 1 1 .... Eso devolverá 1 o DB-Null, dependiendo de si la condición es una coincidencia o no.
eFloh
es agradable. Me gusta el primero.
isxaker
10

Puedes usar:

SELECT COUNT(1) FROM MyTable WHERE ... 

o

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

Esto será más eficiente que SELECT *simplemente seleccionando el valor 1 para cada fila, en lugar de todos los campos.

También hay una sutil diferencia entre COUNT (*) y COUNT (nombre de columna):

  • COUNT(*) contará todas las filas, incluidas las nulas
  • COUNT(column name)solo contará las ocurrencias no nulas del nombre de la columna
Winston Smith
fuente
2
Está asumiendo erróneamente que un DBMS verificará de alguna manera todas esas columnas. La diferencia de rendimiento entre count(1)y count(*)será diferente solo en los DBMS con mayor muerte cerebral.
paxdiablo
2
No, estoy diciendo que usted en realidad se está confiando en los detalles de implementación cuando dice que va a ser más eficiente. Si realmente desea asegurarse de obtener el mejor rendimiento, debe perfilarlo para la implementación específica utilizando datos representativos, o simplemente olvidarlo por completo. Cualquier otra cosa es potencialmente engañosa y podría cambiar drásticamente al cambiar (por ejemplo) de DB2 a MySQL.
paxdiablo
1
Quiero dejar en claro que no estoy ignorando tu respuesta. Que es útil. Lo único con lo que estoy en desacuerdo es con el reclamo de eficiencia, ya que hemos realizado evaluaciones en DB2 / z y descubrimos que no hay una diferencia real entre count(*)y count(1). Si ese es el caso de otros DBMS ', no puedo decir.
paxdiablo
3
"Cualquier otra cosa es potencialmente engañosa y podría cambiar drásticamente al cambiar (por ejemplo) de DB2 a MySQL" Es mucho más probable que te muerda la degradación del rendimiento de SELECT COUNT (*) al mover DBMS que una diferencia de implementación en SELECT 1 o COUNT (1). Creo firmemente en escribir el código que expresa con mayor claridad exactamente lo que desea lograr, en lugar de depender de optimizadores o compiladores para establecer el comportamiento deseado.
Winston Smith el
1
La declaración engañosa "COUNT (*)" significa "contar las filas" en punto final. No requiere acceso a ninguna columna en particular. Y en la mayoría de los casos ni siquiera requerirá acceso a la fila en sí, ya que un conteo es suficiente con cualquier índice único.
James Anderson el
9

Puedes usar:

SELECT 1 FROM MyTable WHERE... LIMIT 1

Se usa select 1para evitar la comprobación de campos innecesarios.

Se usa LIMIT 1 para evitar la comprobación de filas innecesarias.

usuario3059943
fuente
3
Buen punto, pero Limit funciona en MySQL y PostgreSQL, la parte superior funciona en SQL Server, debe tenerlo en cuenta en su respuesta
Leo Gurdian
0

Estoy usando de esta manera:

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

Otra opción:

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