¿Las consultas individuales son más rápidas que las unidas?

44

Pregunta conceptual: ¿Las consultas individuales son más rápidas que las unidas, o: ¿Debería tratar de exprimir cada información que quiero en el lado del cliente en una declaración SELECT o simplemente usar tantas como me parezca conveniente?

TL; DR : Si mi consulta unida lleva más tiempo que la ejecución de consultas individuales, ¿es mi culpa o es de esperar?

En primer lugar, no soy muy conocedor de la base de datos, por lo que puede ser solo yo, pero he notado que cuando tengo que obtener información de varias tablas, es "a menudo" más rápido obtener esta información a través de múltiples consultas en tablas individuales (tal vez que contiene una combinación interna simple) y unir los datos en el lado del cliente para intentar escribir una consulta unida (compleja) donde pueda obtener todos los datos en una consulta.

He tratado de poner un ejemplo extremadamente simple:

Violín de SQL

Configuración del esquema :

CREATE TABLE MASTER 
( ID INT NOT NULL
, NAME VARCHAR2(42 CHAR) NOT NULL
, CONSTRAINT PK_MASTER PRIMARY KEY (ID)
);

CREATE TABLE DATA
( ID INT NOT NULL
, MASTER_ID INT NOT NULL
, VALUE NUMBER
, CONSTRAINT PK_DATA PRIMARY KEY (ID)
, CONSTRAINT FK_DATA_MASTER FOREIGN KEY (MASTER_ID) REFERENCES MASTER (ID)
);

INSERT INTO MASTER values (1, 'One');
INSERT INTO MASTER values (2, 'Two');
INSERT INTO MASTER values (3, 'Three');

CREATE SEQUENCE SEQ_DATA_ID;

INSERT INTO DATA values (SEQ_DATA_ID.NEXTVAL, 1, 1.3);
INSERT INTO DATA values (SEQ_DATA_ID.NEXTVAL, 1, 1.5);
INSERT INTO DATA values (SEQ_DATA_ID.NEXTVAL, 1, 1.7);
INSERT INTO DATA values (SEQ_DATA_ID.NEXTVAL, 2, 2.3);
INSERT INTO DATA values (SEQ_DATA_ID.NEXTVAL, 3, 3.14);
INSERT INTO DATA values (SEQ_DATA_ID.NEXTVAL, 3, 3.7);

Consulta A :

select NAME from MASTER
where ID = 1

Resultados :

| NAME |
--------
|  One |

Consulta B :

select ID, VALUE from DATA
where MASTER_ID = 1

Resultados :

| ID | VALUE |
--------------
|  1 |   1.3 |
|  2 |   1.5 |
|  3 |   1.7 |

Consulta C :

select M.NAME, D.ID, D.VALUE 
from MASTER M INNER JOIN DATA D ON M.ID=D.MASTER_ID
where M.ID = 1

Resultados :

| NAME | ID | VALUE |
---------------------
|  One |  1 |   1.3 |
|  One |  2 |   1.5 |
|  One |  3 |   1.7 |

Por supuesto, no medí ningún rendimiento con estos, pero uno puede observar:

  • La consulta A + B devuelve la misma cantidad de información utilizable que la consulta C.
  • A + B tiene que devolver 1 + 2x3 == 7 "Celdas de datos" al cliente
  • C tiene que devolver 3x3 == 9 "Celdas de datos" al cliente, porque con la unión, naturalmente, incluyo algo de redundancia en el conjunto de resultados.

Generalizando a partir de esto (tan descabellado como sea):

Una consulta unida siempre debe devolver más datos que las consultas individuales que reciben la misma cantidad de información. Dado que la base de datos tiene que improvisar los datos, para grandes conjuntos de datos se puede suponer que la base de datos tiene que hacer más trabajo en una sola consulta unida que en las consultas individuales, ya que (al menos) tiene que devolver más datos al cliente.

¿De esto se deduce que cuando observo que dividir una consulta del lado del cliente en múltiples consultas produce un mejor rendimiento, este es el camino a seguir, o más bien significa que arruiné la consulta unida?

Martín
fuente
Los comentarios no son para discusión extendida; Esta conversación se ha movido al chat .
Jack Douglas
1
Ejecuté un punto de referencia y publiqué los resultados en un artículo en Medium . Hubiera agregado una respuesta aquí, pero ya lo hice en otra pregunta , y publicar la misma respuesta a varias preguntas está mal visto .
Benjamin

Respuestas:

45

¿Las consultas individuales son más rápidas que las unidas, o: ¿Debería tratar de exprimir cada información que quiero en el lado del cliente en una declaración SELECT o simplemente usar tantas como me parezca conveniente?

En cualquier escenario de rendimiento, debe probar y medir las soluciones para ver cuál es más rápido .

Dicho esto, casi siempre se da el caso de que un conjunto de resultados unidos de una base de datos ajustada correctamente será más rápido y escalará mejor que devolver las filas de origen al cliente y luego unirlas allí. En particular, si los conjuntos de entrada son grandes y el conjunto de resultados es pequeño, piense en la siguiente consulta en el contexto de ambas estrategias: unir dos tablas de 5 GB cada una, con un conjunto de resultados de 100 filas. Eso es un extremo, pero ya ves mi punto.

Me di cuenta de que cuando tengo que obtener información de varias tablas, es "a menudo" más rápido obtener esta información a través de múltiples consultas en tablas individuales (quizás conteniendo una unión interna simple) y parchear los datos en el lado del cliente para intentar escribir una consulta unida (compleja) donde pueda obtener todos los datos en una consulta.

Es muy probable que el esquema o los índices de la base de datos se puedan mejorar para atender mejor las consultas que le está enviando.

Una consulta unida siempre debe devolver más datos que las consultas individuales que reciben la misma cantidad de información.

Por lo general, este no es el caso. La mayoría de las veces, incluso si los conjuntos de entrada son grandes, el conjunto de resultados será mucho más pequeño que la suma de las entradas.

Dependiendo de la aplicación, los conjuntos de resultados de consultas muy grandes que se devuelven al cliente son una señal de alerta inmediata: ¿qué está haciendo el cliente con un conjunto de datos tan grande que no se puede hacer más cerca de la base de datos? Mostrar 1,000,000 filas a un usuario es altamente sospechoso por decir lo menos. El ancho de banda de la red también es un recurso finito.

Dado que la base de datos tiene que improvisar los datos, para grandes conjuntos de datos se puede suponer que la base de datos tiene que hacer más trabajo en una sola consulta unida que en las consultas individuales, ya que (al menos) tiene que devolver más datos al cliente.

No necesariamente. Si los datos se indexan correctamente, es más probable que la operación de unión se realice de manera más eficiente en la base de datos sin necesidad de escanear una gran cantidad de datos. Además, los motores de bases de datos relacionales están especialmente optimizados en un nivel bajo para unirse ; las pilas de clientes no lo son.

¿De esto se deduce que cuando observo que dividir una consulta del lado del cliente en múltiples consultas produce un mejor rendimiento, este es el camino a seguir, o más bien significa que arruiné la consulta unida?

Como usted dijo que no tiene experiencia en lo que respecta a las bases de datos, sugeriría que aprenda más sobre el diseño de la base de datos y el ajuste del rendimiento. Estoy bastante seguro de que ahí es donde radica el problema aquí. También son posibles consultas SQL escritas de manera ineficiente, pero con un esquema simple es menos probable que sea un problema.

Ahora, eso no quiere decir que no haya otras formas de mejorar el rendimiento. Hay escenarios en los que puede optar por escanear un conjunto de datos de mediano a grande y devolverlo al cliente si la intención es utilizar algún tipo de mecanismo de almacenamiento en caché. El almacenamiento en caché puede ser excelente, pero introduce complejidad en su diseño. El almacenamiento en caché puede incluso no ser apropiado para su aplicación.

Una cosa que no se ha mencionado en ninguna parte es mantener la coherencia en los datos que devuelve la base de datos. Si se utilizan consultas separadas, es más probable (debido a muchos factores) que se devuelvan datos inconsistentes, a menos que se use una forma de aislamiento de instantánea para cada conjunto de consultas.

Jon Seigel
fuente
+1 para el ancho de banda de la red también es un recurso finito.
Hari Harker
OP dice que los conjuntos de resultados de datos UNIDOS siempre son más grandes. > Una consulta unida siempre tiene que devolver más datos que las consultas individuales. Creo que esto es objetivamente cierto (para> =), por ejemplo, los conjuntos de resultados difieren en tamaño, por lo que hay más datos por cable. ¿Tienes un ejemplo donde esto no es cierto? Si me uno a Autores -> Publicaciones y Autores tiene un campo llamado "biografía" que es un campo JSON de 1MB, para un Autor de 100 Publicaciones, a través del cable transmitiré 100MB vs 1MB. ¿Esto esta mal?
hytromo
6

Por supuesto, no medí ningún rendimiento con estos

Preparaste un buen código de muestra. ¿Viste el tiempo en SQL Fiddle? Incluso algunas breves pruebas de rendimiento no científicas mostrarán que la consulta tres en su demostración tarda aproximadamente la misma cantidad de tiempo en ejecutarse que la consulta una o dos por separado. La combinación de uno y dos toma aproximadamente el doble de tres y eso es antes de que se realice una unión del lado del cliente.

A medida que aumenta los datos, la velocidad de la consulta uno y dos divergiría, pero la unión de la base de datos aún sería más rápida.

También debe considerar lo que sucedería si la unión interna está eliminando datos.

Leigh Riffel
fuente
2

También se debe considerar el optimizador de consultas. Su función es tomar su SQL declarativo y traducirlo en pasos de procedimiento. Para encontrar la combinación más eficiente de pasos de procedimiento, examinará las combinaciones de uso de índices, tipos, conjuntos de resultados intermedios de almacenamiento en caché y todo tipo de otras cosas también. El número de permutaciones puede ser extremadamente grande incluso con lo que parecen consultas bastante simples.

Gran parte del cálculo realizado para encontrar el mejor plan se debe a la distribución de datos dentro de las tablas. Estas distribuciones se muestrean y almacenan como objetos estadísticos. Si esto está mal, llevan al optimizador a tomar malas decisiones. Las malas elecciones al principio del plan conducen a elecciones aún más pobres más adelante en un efecto de bola de nieve.

No se desconoce si una consulta de tamaño mediano devuelve cantidades modestas de datos que demoran minutos en ejecutarse. La indexación correcta y las buenas estadísticas reducen esto a milisegundos.

Michael Green
fuente
-3

Múltiples consultas es el camino a seguir. Si maneja escenarios simples como ese, la sobrecarga de costos del optimizador de consultas es un factor. Con más datos, entra en juego la ineficiencia de la red (filas redundantes). Solo con muchos más datos hay eficiencia.

Al final, lo que experimenta es algo que muchos desarrolladores ven. Los DBA siempre dicen "no, hacer una unión", pero la realidad es: es más rápido hacer múltiples selecciones simples en este caso.

TomTom
fuente
55
No hay "ineficiencia de red" en una unión: todo sucede en el servidor de la base de datos, por lo que no hay red involucrada (¡a menos que te unas a través de un enlace db!)
Chris Saxon
2
Es posible que desee considerar si la capa de red tiene compresión o no. SQL * Net de Oracle sí, ya que los valores que se repiten en la misma columna se comprimen eficientemente.
David Aldridge
3
@TomTom puede tener un punto o no (como señala David Aldridge, la compresión es importante) pero su redacción es confusa. ¿"ineficiencia de red de la unión" ? Realmente, arregla eso para que sea obvio lo que quieres decir.
ypercubeᵀᴹ
@ChrisSaxon seguro de que hay una imagen que tiene tablas para un informe "título-> base-> tablas-filas" y necesita todas las filas para que pueda unirse a estas 3 tablas. Cada tabla tiene varchars largos, por lo que sucede para cada fila que está repitiendo estos varchars largos. La capa de aplicación necesita asignar memoria para todas estas cadenas y luego agruparlas para su modelo. Así que creo que eso es lo que quiere decir, hay más datos enviados
MIKE
@MIKE que depende de las expresiones que seleccione, no de la combinación. Y puede haber compresión de red. En Oracle Database SQL * Net elimina valores duplicados repetidos nicetheory.io/2018/01/11/…
Chris Saxon