La forma más rápida de determinar si existe un registro

143

Como sugiere el título ... Estoy tratando de descubrir la forma más rápida con la menor sobrecarga para determinar si existe un registro en una tabla o no.

Consulta de muestra:

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 ?se intercambia con 'TB100'... tanto la primera como la segunda consulta devolverán exactamente el mismo resultado (digamos ... 1para esta conversación). La última consulta volverá 'TB100'como se esperaba, o nada si idno está presente en la tabla.

El propósito es averiguar si el idestá en la tabla o no. De lo contrario, el programa insertará luego el registro; si es así, el programa lo omitirá o realizará una consulta de ACTUALIZACIÓN basada en otra lógica del programa fuera del alcance de esta pregunta.

¿Cuál es más rápido y tiene menos gastos generales? (Esto se repetirá decenas de miles de veces por ejecución de programa y se ejecutará muchas veces al día).

(Ejecutar esta consulta en M $ SQL Server desde Java a través del controlador JDBC proporcionado por M $)

SnakeDoc
fuente
1
Esto podría depender de la base de datos. Por ejemplo, contar con Postgres es bastante lento.
Mike Christensen
Lo sentimos, esto es Java hablando con M $ SQL a través del controlador jdbc. Actualizaré mi OP.
SnakeDoc
2
Hay existe también.
Nikola Markovinović
@Nikola Markovinović: ¿cómo lo usarías en este caso?
zerkms
55
@zerkms Depende del contexto. Si en el procedimiento almacenado sería if exists(select null from products where id = @id); si en una consulta llamada directamente por un cliente select case when exists (...) then 1 else 0 end.
Nikola Markovinović

Respuestas:

170

SELECT TOP 1 products.id FROM products WHERE products.id = ?; superará todas sus sugerencias, ya que terminará la ejecución después de encontrar el primer registro.

Declan_K
fuente
55
¿El optimizador no lo tiene en cuenta cuando busca a través de PK (o cualquier otra clave única)?
zerkms
3
Nunca dijo que era el PK, pero si es así, sí, el optimizador lo tendría en cuenta.
Declan_K
3
@Declan_K: parece que mi esfera mágica falló en este caso y una columna titulada como idno es PK. Entonces +1 a tu consejo.
zerkms
44
Si no es el PK, también sugeriría asegurarse de que haya un índice en esa columna. De lo contrario, la consulta tendrá que hacer un escaneo de tabla en lugar de una búsqueda de tabla más rápida.
CD Jorgensen
3
Creo que deberíamos considerar la respuesta de @ nenad-zivkovic sobre esta.
Giulio Caccin
192

EXISTS(o NOT EXISTS) está especialmente diseñado para verificar si algo existe y, por lo tanto, debería ser (y es) la mejor opción. Se detendrá en la primera fila que coincida, por lo que no requiere una TOPcláusula y en realidad no selecciona ningún dato, por lo que no hay sobrecarga en el tamaño de las columnas. Puede usarlo con seguridad SELECT *aquí, no es diferente de SELECT 1, SELECT NULLo SELECT AnyColumn... (incluso puede usar una expresión no válida como SELECT 1/0y no se romperá) .

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
fuente
¿No tiene esto que ejecutar primero la instrucción SELECT, luego ejecutar la instrucción IF EXISTS ... causando una sobrecarga adicional y, por lo tanto, más tiempo de procesamiento?
SnakeDoc
77
@SnakeDoc No. Existsfunciona selectde tal manera que sale tan pronto como se encuentra una fila. Además, existe simplemente nota la existencia de registros, no valores reales en el registro, lo que ahorra la necesidad de cargar la fila desde el disco (suponiendo que los criterios de búsqueda estén indexados, por supuesto). En cuanto a los gastos generales de if- tendrá que pasar este tiempo minúsculo de todos modos.
Nikola Markovinović
1
@ NikolaMarkovinović punto interesante. No estoy seguro de si existe un índice en este campo, y mi nuevo SQL no sabe cómo averiguarlo. Estoy trabajando con esta base de datos de Java a través de JDBC y la base de datos está ubicada de forma remota en un colo en algún lugar. Solo se me ha proporcionado un "resumen de la base de datos" que solo detalla qué campos existen en cada tabla, su tipo y cualquier FK o PK. ¿Esto cambia algo?
SnakeDoc
3
@SnakeDoc Para averiguar acerca de la estructura de tablas, incluyendo las claves y los índices de ejecución extranjeros, sp_help nombre_tabla . Los índices son esenciales cuando se trata de recuperar algunas filas de muchas, además de usar select topo exists; si no están presentes, el motor sql tendrá que realizar un escaneo de tabla. Esta es la opción de búsqueda de tabla menos deseable. Si no está autorizado para crear índices, deberá comunicarse con el personal técnico del otro lado para averiguar si los ajustan automáticamente o si esperan que sugiera índices.
Nikola Markovinović
1
@ Konstantin Puedes hacer algo comoSELECT CASE WHEN EXISTS(..) THEN 1 ELSE 0 END;
Nenad Zivkovic
21

Nada puede vencer

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

No necesita contar para saber si hay datos en la tabla. Y no use alias cuando no sea necesario.

AgentSQL
fuente
55
A pesar de su nombre idno es clave primaria. Entonces, aunque no esté contando , aún necesita encontrar todos los registros coincidentes, posiblemente miles de ellos. Acerca del alias: el código es un trabajo constante en progreso. Nunca se sabe cuándo tendrá que regresar. El alias ayuda a prevenir errores estúpidos de tiempo de ejecución; por ejemplo, un nombre de columna único que no necesitaba un alias ya no es único porque alguien creó una columna con el mismo nombre en otra tabla unida.
Nikola Markovinović
Sí, tienes toda la razón. Aliasing ayuda mucho, pero no creo que haga ninguna diferencia cuando no se usan combinaciones. Entonces, dije que no lo use si no es necesario. :) Y puedes encontrar una larga discusión aquí sobre cómo verificar la existencia. :)
AgentSQL
3
No sé por qué acepté el término aliasing. El término correcto es qualifying. Aquí hay una explicación más larga de Alex Kuznetzov . Acerca de las consultas de una sola mesa - es única tabla ahora . Pero más tarde, cuando se descubre un error y está tratando de contener la inundación, el cliente está nervioso, se une a otra mesa solo para enfrentar un mensaje de error, un mensaje fácilmente corregible, pero no en este momento sudoroso, ocurre un pequeño derrame cerebral y corrige el problema. error recordando nunca dejar una columna ...
Nikola Markovinović
1
No puedo ignorar eso ahora. ¡¡Gracias!! :)
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

Este enfoque le devuelve un valor booleano.

Kris Coleman
fuente
1
Probablemente pueda omitir la instrucción Top y la instrucción * para hacerlo un poco más rápido, ya que Exist se cerrará una vez que encuentre un registro, así que algo como esto: SELECCIONE EL CASO CUANDO EXISTE (SELECCIONE 1 DE dbo. [YourTable] WHERE [YourColumn] = [YourValue]) ENTONCES CAST (1 COMO BIT) ELSE CAST (0 COMO BIT) FIN
Stefan Zvonar
Esta sugerencia no menciona por qué esto sería más rápido sobre las declaraciones incorporadas existentes / no existentes dentro de SQL Server. Sin ninguna evaluación comparativa, sería difícil creer que una declaración de caso produciría un resultado más rápido que una respuesta verdadera / falsa inmediata.
Bonez024
8

También puedes usar

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

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

No piense que nadie lo ha mencionado todavía, pero si está seguro de que los datos no cambiarán debajo de usted, es posible que también desee aplicar la sugerencia NoLock para asegurarse de que no se bloquee durante la lectura.

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
fuente
3
SELECT COUNT(*) FROM products WHERE products.id = ?;

Esta es la solución de base de datos relacional cruzada que funciona en todas las bases de datos.

muchacho rebelde
fuente
66
Sin embargo, obliga a la base de datos a recorrer todos los registros, muy lento en tablas grandes
amd
@amd cuidado de explicar por qué?
UmNyobe
@amd tu comentario tiene mucho sentido. Esta consulta es más ENCONTRAR TODO que ENCONTRAR CUALQUIERA.
UmNyobe
1

A continuación se muestra la forma más simple y rápida de determinar si existe un registro en la base de datos o no. Lo bueno es que funciona en todos los DB relacionales

SELECT distinct 1 products.id FROM products WHERE products.id = ?;
Prasad varonil
fuente
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
fuente
2
Posiblemente su código funcione muy bien, pero sería mejor si agrega información adicional para que sea mejor comprensible.
idmean
0

Lo he usado en el pasado y no requiere un escaneo completo de la tabla para ver si existe algo. Es super rápido ...

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

Para aquellos que se topan con esto desde MySQL u Oracle, MySQL admite la cláusula LIMIT para seleccionar un número limitado de registros, mientras que Oracle usa ROWNUM.

Werner
fuente