Rendimiento de a = 0 y b = 0 y ... z = 0 vs a + b + c + d = 0

20

Esta es una pregunta simple para la que parece que no puedo encontrar la respuesta.

En términos de rendimiento, si tengo una WHEREcláusula como a=0 and b=0 and ... z=0, ¿ganaría algún rendimiento si reemplazara esa condición cona+b+...+z=0 ?

En otras palabras, ¿hay alguna ganancia de rendimiento al reemplazar lo siguiente

Select * 
From MyTable 
Where A=0 and B=0 and C=0 and D=0...

Con

Select * 
From MyTable 
Where A+B+C+D=0...

Sé que puede depender de índices, pero para este propósito, digamos que no existen índices. ¿El operador aritmético (+) funciona mejor que un operador lógico "OR" o "AND"?

Tengo la impresión de que la adición funciona mejor que múltiples condiciones con AND u OR.

Resultados de la prueba

En una tabla de 4.2 millones de filas

Retornando filas donde A = 0 B = 0 y C = 0 -> 351748 Filas

La adición (A + B + C = 0) tomó 5 segundos mientras que las condiciones lógicas A = 0 y B = 0 y C = 0 tomaron 11 segundos.

Por otra parte

Retornando filas donde A <> 0 B <> 0 o C <> 0 -> 3829750 Filas 58 segundos

Filas de retorno donde F65 + F67 + f64 <> 0 -> 3829750 Filas 57 segundos

Para el quirófano, parece que no hay una diferencia significativa.

Estoy de acuerdo con gbn:

Si A es -1 y B es 1, A + B = 0 pero A = 0 y B = 0 es falso

y con AMtwo:

ABS (A) + ABS (B) + ABS (C) + ABS (D) ... Incluso si espera solo valores positivos, si la columna acepta valores negativos, debe suponer que puede encontrar uno

Los resultados son muy impresionantes, como pensé, parece que la adición es mucho más rápida que los operadores lógicos.

A = Flotar, B = Dinero y C = Flotar. La consulta utilizada es como se muestra. En mi caso, todos son números positivos. Sin índices ¡Es lógico en mi mente que la adición sea más rápida que las condiciones lógicas!

JohnG
fuente
¿Son estos booleanos? ¿De cuántas columnas está hablando 4 (en los ejemplos) o 26 (en el título)? Hace la diferencia ¿Qué versión de SQL Server? ¿Dónde entran en juego FLOAT y DINERO? ¿Cuántas filas estamos suponiendo? Esta pregunta tiene muchos factores.
Evan Carroll
@Evan Carroll No son booleanos, son números no indexados (int, float, money, etc.). Independientemente de la versión SQL (SQL2012 y posteriores), el número de filas o columnas, la pregunta era averiguar qué operador funciona mejor: operadores lógicos frente a operadores aritméticos. Como puede ver, Max Vernon demuestra perfectamente la teoría con sus ejemplos.
JohnG

Respuestas:

46

En su pregunta, detalla algunas pruebas que ha preparado donde "prueba" que la opción de adición es más rápida que comparar las columnas discretas. Sospecho que su metodología de prueba puede ser defectuosa de varias maneras, como han aludido @gbn y @srutzky.

Primero, debe asegurarse de que no está probando SQL Server Management Studio (o cualquier cliente que esté usando). Por ejemplo, si está ejecutando un archivo SELECT *desde una tabla con 3 millones de filas, está probando principalmente la capacidad de SSMS para extraer filas de SQL Server y representarlas en la pantalla. Es mucho mejor usar algo como lo SELECT COUNT(1)que niega la necesidad de extraer millones de filas en la red y representarlas en la pantalla.

En segundo lugar, debe tener en cuenta la caché de datos de SQL Server. Por lo general, probamos la velocidad de leer datos del almacenamiento y procesarlos desde un caché en frío (es decir, los almacenamientos intermedios de SQL Server están vacíos). Ocasionalmente, tiene sentido hacer todas sus pruebas con un caché cálido, pero debe abordar sus pruebas explícitamente con eso en mente.

Para una prueba de memoria caché en frío, debe ejecutar CHECKPOINTy DBCC DROPCLEANBUFFERSantes de cada ejecución de la prueba.

Para la prueba que ha preguntado en su pregunta, creé el siguiente banco de pruebas:

IF COALESCE(OBJECT_ID('tempdb..#SomeTest'), 0) <> 0
BEGIN
    DROP TABLE #SomeTest;
END
CREATE TABLE #SomeTest
(
    TestID INT NOT NULL
        PRIMARY KEY 
        IDENTITY(1,1)
    , A INT NOT NULL
    , B FLOAT NOT NULL
    , C MONEY NOT NULL
    , D BIGINT NOT NULL
);

INSERT INTO #SomeTest (A, B, C, D)
SELECT o1.object_id, o2.object_id, o3.object_id, o4.object_id
FROM sys.objects o1
    , sys.objects o2
    , sys.objects o3
    , sys.objects o4;

SELECT COUNT(1) 
FROM #SomeTest;

Esto devuelve un recuento de 260,144,641 en mi máquina.

Para probar el método de "adición", ejecuto:

CHECKPOINT 5;
DBCC FREEPROCCACHE;
DBCC DROPCLEANBUFFERS;

SET STATISTICS IO, TIME ON;
GO
SELECT COUNT(1)
FROM #SomeTest st
WHERE (st.A + st.B + st.C + st.D) = 0;
GO
SET STATISTICS IO, TIME OFF;

La pestaña de mensajes muestra:

Tabla '#SomeTest'. Cuenta de escaneo 3, lecturas lógicas 1322661, lecturas físicas 0, lecturas anticipadas 1313877, lecturas lógicas lob 0, lecturas físicas lob 0, lecturas anticipadas lob 0.

Tiempos de ejecución de SQL Server: tiempo de CPU = 49047 ms, tiempo transcurrido = 173451 ms.

Para la prueba de "columnas discretas":

CHECKPOINT 5;
DBCC FREEPROCCACHE;
DBCC DROPCLEANBUFFERS;

SET STATISTICS IO, TIME ON;
GO
SELECT COUNT(1)
FROM #SomeTest st
WHERE st.A = 0
    AND st.B = 0
    AND st.C = 0
    AND st.D = 0;
GO

SET STATISTICS IO, TIME OFF;

nuevamente, desde la pestaña de mensajes:

Tabla '#SomeTest'. Cuenta de escaneo 3, lecturas lógicas 1322661, lecturas físicas 0, lecturas anticipadas 1322661, lecturas lógicas lob 0, lecturas físicas lob 0, lecturas anticipadas lob 0.

Tiempos de ejecución de SQL Server: tiempo de CPU = 8938 ms, tiempo transcurrido = 162581 ms.

De las estadísticas anteriores, puede ver la segunda variante, con las columnas discretas en comparación con 0, el tiempo transcurrido es aproximadamente 10 segundos más corto y el tiempo de CPU es aproximadamente 6 veces menor. Las largas duraciones en mis pruebas anteriores son principalmente el resultado de leer muchas filas del disco. Si baja el número de filas a 3 millones, verá que las proporciones siguen siendo las mismas, pero los tiempos transcurridos disminuyen notablemente, ya que la E / S del disco tiene un efecto mucho menor.

Con el método "Adición":

Tabla '#SomeTest'. Recuento de escaneo 3, lecturas lógicas 15255, lecturas físicas 0, lecturas anticipadas 0, lecturas lógicas lob 0, lecturas físicas lob 0, lecturas anticipadas lob 0.

Tiempos de ejecución de SQL Server: tiempo de CPU = 499 ms, tiempo transcurrido = 256 ms.

Con el método de "columnas discretas":

Tabla '#SomeTest'. Recuento de escaneo 3, lecturas lógicas 15255, lecturas físicas 0, lecturas anticipadas 0, lecturas lógicas lob 0, lecturas físicas lob 0, lecturas anticipadas lob 0.

Tiempos de ejecución de SQL Server: tiempo de CPU = 94 ms, tiempo transcurrido = 53 ms.

¿Qué marcará una gran diferencia para esta prueba? Un índice apropiado, como:

CREATE INDEX IX_SomeTest ON #SomeTest(A, B, C, D);

El método de "suma":

Tabla '#SomeTest'. Cuenta de escaneo 3, lecturas lógicas 14235, lecturas físicas 0, lecturas de lectura anticipada 0, lecturas lógicas lob 0, lecturas físicas lob 0, lecturas de lectura lob 0.

Tiempos de ejecución de SQL Server: tiempo de CPU = 546 ms, tiempo transcurrido = 314 ms.

El método de "columnas discretas":

Tabla '#SomeTest'. Cuenta de escaneo 1, lecturas lógicas 3, lecturas físicas 0, lecturas de lectura anticipada 0, lecturas lógicas lob 0, lecturas físicas lob 0, lecturas de lectura lob 0.

Tiempos de ejecución de SQL Server: tiempo de CPU = 0 ms, tiempo transcurrido = 0 ms.

El plan de ejecución para cada consulta (con el índice anterior en el lugar) es bastante revelador.

El método de "adición", que debe realizar un análisis de todo el índice:

ingrese la descripción de la imagen aquí

y el método de "columnas discretas", que puede buscar la primera fila del índice donde está la columna de índice inicial A, es cero:

ingrese la descripción de la imagen aquí

Max Vernon
fuente
24

Supongamos que tiene un índice en A, B, C y D. También podría filtrarse.

Es más probable que use el índice que la suma.

Where A=0 and B=0 and C=0 and D=0

En otras noticias, si A es -1 y B es 1, A+B=0es verdadero pero A=0 and B=0es falso.

gbn
fuente
7

(Tenga en cuenta que esta respuesta se envió antes de que se observara cualquier prueba en la pregunta: el texto de la pregunta terminó justo por encima de los resultados de la prueba sección de ).

Supongo que ANDse preferirían las condiciones separadas ya que el optimizador sería más propenso a cortocircuitar la operación si uno de ellos no es igual a 0, sin necesidad de hacer un cálculo primero.

Aún así, dado que se trata de una cuestión de rendimiento, primero debe configurar una prueba para determinar la respuesta en su hardware. Informe esos resultados, muestre su código de prueba y solicite a otros que lo revisen para asegurarse de que fue una buena prueba. Puede haber otros factores dignos de consideración en los que no pensó.

Solomon Rutzky
fuente
3

Algún razonamiento general, si no tiene ningún índice a mano, no creo que importe mucho cuál de las dos soluciones que elija, ambas funcionarán mal. Si, por otro lado, tiene un índice en una o más de las columnas del predicado, es probable que la primera tenga un mejor rendimiento que la segunda, ya que la segunda probablemente no podrá utilizar los índices.

Las disyunciones (OR) en general funcionan peor que las conjunciones (AND), pero incluso si tiene una consulta con disyunciones, pondré mi dinero en la primera.

Lennart
fuente
2

Esta es una pregunta simple

No, no es. Esta (clase de) pregunta es lo que afecta a muchos DBA y desarrolladores de software día a día, y es casi trivial.

para el que parece que no puedo encontrar la respuesta.

Si, no lo harás. Al menos no una respuesta general. En primer lugar, dependerá en gran medida de qué RDBMS esté utilizando (OK, está utilizando , pero aún así). Incluso puede cambiar cuando pasa de una versión de su RDBMS a la siguiente.

Entonces, puede depender de cualquier cantidad de otros pequeños detalles, por ejemplo, cómo su base de datos almacena los datos, si tiene sub-selecciones / uniones que confunden el problema para el plan optimizador, etc. El optimizador puede darle diferentes planes de ejecución dependiendo en cuántas filas tienes ...

Hacer una prueba del mundo real suele ser la única forma útil de resolver preguntas como esta. Además, cualquier ganancia obtenida por optimizaciones "arcanas" como esta generalmente se absorbe diez veces por la elección inteligente de los índices, por lo que no me molestaría en pasar demasiado tiempo en ello, antes de que realmente se descarte el uso de índices.

AnoE
fuente
0

Esto puede ser obvio, pero si las columnas lo son INT, a+b+cpodría ser igual a cero incluso cuando ninguna de ellas sea realmente cero. ¡Estás probando dos cosas diferentes!

Ross Presser
fuente
Me acabo de dar cuenta de que @gbn mencionó esto en su respuesta.
Ross Presser