ÚNETE consultas versus consultas múltiples


180

¿Las consultas JOIN son más rápidas que varias consultas? (Ejecutas tu consulta principal, y luego ejecutas muchos otros SELECT basados ​​en los resultados de tu consulta principal)

Lo pregunto porque UNIRSE a ellos complicaría MUCHO el diseño de mi aplicación

Si son más rápidos, ¿alguien puede aproximarse más o menos por cuánto? Si es 1.5x, no me importa, pero si es 10x, supongo que sí.


Supongo que serían más rápidos. Sé que un INSERT en comparación con decir 10 consultas INSERT individuales es mucho más rápido.
alex

1
Puede ser importante si sus múltiples consultas están dentro de un procedimiento almacenado o si se originan en la aplicación (edite su pregunta con esta información). El primero será mucho más rápido que el segundo.
colithium 01 de

Respuestas:


82

Esto es demasiado vago para darle una respuesta relevante a su caso específico. Depende de muchas cosas. Jeff Atwood (fundador de este sitio) en realidad escribió sobre esto . Sin embargo, en su mayor parte, si tiene los índices correctos y realiza correctamente sus uniones, generalmente será más rápido hacer 1 viaje que varios.


2
Si está uniendo 3 o más tablas en diferentes claves, a menudo las bases de datos (es decir, mysql) solo pueden usar un índice por tabla, lo que significa que tal vez una de las uniones será rápida (y use un índice) mientras que las otras serán extremadamente lentas. Para consultas múltiples, puede optimizar los índices para usar en cada consulta.
user151975

44
Creo que esto depende de su definición de "más rápido" ... por ejemplo, 3 uniones internas de PK pueden dar la vuelta más rápido que 4 viajes de ida y vuelta, debido a la sobrecarga de la red, y porque necesita detenerse y prepararse y enviar cada consulta después del consulta previa completada. Sin embargo, si comparara un servidor bajo carga, en la mayoría de los casos, las uniones tomarán más tiempo de CPU frente a las consultas PK, y a menudo también causan más sobrecarga de red.
mindplay.dk

97

Para las uniones internas, una única consulta tiene sentido, ya que solo obtiene filas coincidentes. Para las uniones izquierdas, las consultas múltiples son mucho mejores ... mira el siguiente punto de referencia que hice:

  1. Consulta única con 5 uniones

    consulta: 8.074508 segundos

    tamaño del resultado: 2268000

  2. 5 consultas seguidas

    tiempo de consulta combinado: 0.00262 segundos

    tamaño del resultado: 165 (6 + 50 + 7 + 12 + 90)

.

Tenga en cuenta que obtenemos los mismos resultados en ambos casos (6 x 50 x 7 x 12 x 90 = 2268000)

las uniones izquierdas usan exponencialmente más memoria con datos redundantes.

Es posible que el límite de memoria no sea tan malo si solo hace una combinación de dos tablas, pero generalmente tres o más y vale la pena realizar consultas diferentes.

Como nota al margen, mi servidor MySQL está justo al lado de mi servidor de aplicaciones ... por lo que el tiempo de conexión es insignificante. Si su tiempo de conexión es en segundos, entonces quizás haya un beneficio

Franco


31
Si dejamos de lado el molesto hecho de que nadie en su sano juicio hace una combinación cruzada entre 5 tablas (por esa misma razón, junto con eso en la mayoría de los casos simplemente no tiene sentido ), su "punto de referencia" podría tener algún mérito . Pero las uniones izquierdas o internas son la norma, generalmente por clave (lo que hace que la recuperación sea mucho más rápida), y la duplicación de datos suele ser mucho, mucho menos de lo que parece ser.
cHao

12
@cHao dice quién? Acabo de buscar SMF y phpBB y vi JOINs entre 3 tablas: si agrega complementos o modificaciones, podrían agregarse fácilmente a eso. Cualquier tipo de aplicación grande tiene el potencial de muchas uniones. Podría decirse que un ORM mal escrito / mal utilizado podría UNIR tablas que realmente no necesita (quizás incluso todas las tablas).
Natalie Adams

55
@NathanAdams: las uniones izquierdas e internas no son malas en absoluto. (De hecho, si no está uniendo tablas aquí y allá, está haciendo SQL incorrectamente). De lo que estaba hablando es de combinaciones cruzadas , que casi siempre son indeseables incluso entre dos tablas, y mucho menos 5, y que ser la única forma de obtener los resultados "2268000", que de otro modo serían totalmente falsos, mencionados anteriormente.
cHao

2
Mira los resultados, sin embargo. "tamaño del resultado: 2268000" versus "tamaño del resultado: 165". Creo que su desaceleración con JOIN se debe a que sus registros tienen una relación de uno a muchos entre sí, mientras que si tuvieran una relación de uno a uno, el JOIN sería absolutamente mucho más rápido y ciertamente no tendría un resultado tamaño más grande que el SELECCIONAR.
HoldOffHunger

3
@cHao Obviamente no has conocido a Magento en el momento de tu primer comentario
vitoriodachef

26

Esta pregunta es antigua, pero le faltan algunos puntos de referencia. Comparé JOIN contra sus 2 competidores:

  • N + 1 consultas
  • 2 consultas, la segunda con un WHERE IN(...)o equivalente

El resultado es claro: en MySQL, JOINes mucho más rápido. Las consultas N + 1 pueden reducir drásticamente el rendimiento de una aplicación:

ÚNETE vs DONDE EN N vs 1

Es decir, a menos que seleccione muchos registros que apunten a un número muy pequeño de registros extranjeros distintos. Aquí hay un punto de referencia para el caso extremo:

JOIN vs N + 1: todos los registros apuntan al mismo registro extranjero

Es muy poco probable que esto suceda en una aplicación típica, a menos que se una a una relación de muchos, en cuyo caso la clave externa está en la otra tabla, y está duplicando los datos de la tabla principal muchas veces.

Para llevar:

  • Para las relaciones * a uno, use siempre JOIN
  • Para las relaciones de * a muchos, una segunda consulta puede ser más rápida

Vea mi artículo en Medium para más información.


22

De hecho, llegué a esta pregunta buscando una respuesta, y después de leer las respuestas dadas, solo puedo estar de acuerdo en que la mejor manera de comparar el rendimiento de las consultas DB es obtener números del mundo real porque solo hay que tener en cuenta muchas variables PERO, también creo que comparar los números entre ellos no sirve de nada en casi todos los casos. Lo que quiero decir es que los números siempre deben compararse con un número aceptable y definitivamente no compararse entre sí.

Puedo entender si una forma de consulta lleva unos 0.02 segundos y la otra toma 20 segundos, esa es una gran diferencia. Pero, ¿qué sucede si una forma de consulta tarda 0.0000000002 segundos y la otra tarda 0.0000002 segundos? En ambos casos, una forma es la friolera 1000 veces más rápida que la otra, pero ¿ realmente sigue siendo "feroz" en el segundo caso?

En pocas palabras, como lo veo personalmente: si funciona bien, busque la solución fácil.


44
Eso, por supuesto, dependiendo de si planeas o no escalar. Porque cuando Facebook comenzó, estoy seguro de que tenían ese tipo de consultas, pero tenía en cuenta la escala y buscó la solución más eficiente, aunque posiblemente más compleja.
dudewad

@dudewad tiene sentido. Todo depende de lo que necesites, al final.
Valentin Flachsel

44
Jaja sí ... porque en google 1 nanosegundo perdido es literalmente igual a algo así como 10 billones de billones de dólares ... pero eso es solo un rumor.
dudewad

2
@dudewad En realidad, cuando Facebook comenzó, les garantizo que eligieron la solución más simple. Zuckerberg dijo que programó la primera versión en solo 2 semanas. Las nuevas empresas deben moverse rápido para competir y las que sobreviven generalmente no se preocupan por escalar hasta que realmente lo necesitan. Luego refactorizan cosas después de que tienen millones de dólares de inversión y pueden contratar programadores de rockstar que se especializan en rendimiento. Según su punto de vista, esperaría que Facebook a menudo busque la solución más compleja para ganancias de rendimiento diminutas ahora, pero la mayoría de nosotros no estamos programando Facebook.
dallin el

15

Hice una prueba rápida seleccionando una fila de una tabla de 50,000 filas y uniéndola con una fila de una tabla de 100,000 filas. Básicamente se parecía a:

$id = mt_rand(1, 50000);
$row = $db->fetchOne("SELECT * FROM table1 WHERE id = " . $id);
$row = $db->fetchOne("SELECT * FROM table2 WHERE other_id = " . $row['other_id']);

vs

$id = mt_rand(1, 50000);
$db->fetchOne("SELECT table1.*, table2.*
    FROM table1
    LEFT JOIN table1.other_id = table2.other_id
    WHERE table1.id = " . $id);

Los dos métodos de selección tomaron 3.7 segundos para 50,000 lecturas, mientras que JOIN tardó 2.0 segundos en mi computadora lenta en casa. INNER JOIN y LEFT JOIN no hicieron la diferencia. Obtener varias filas (por ejemplo, usando IN SET) arrojó resultados similares.


1
Tal vez la diferencia podría cambiar de otra manera si selecciona una página de filas (como 20 o 50) como para una cuadrícula de vista web típica, y compara unir a la IZQUIERDA individual con dos consultas: seleccionar 2 o 3 identificadores con algunos criterios WHERE y luego ejecutar el otro Consulta SELECT con IN ().
JustAMartin

¿Están indexadas las columnas id y other_id?
Aarish Ramesh

11

La verdadera pregunta es: ¿Estos registros tienen una relación uno a uno o una relación uno a muchos ?

Respuesta TLDR:

Si es uno a uno, use una JOINdeclaración.

Si es uno a muchos, use una (o muchas) SELECTdeclaraciones con optimización de código del lado del servidor.

Por qué y cómo usar SELECT para la optimización

SELECT'ing (con múltiples consultas en lugar de uniones) en un gran grupo de registros basado en una relación uno a muchos produce una eficiencia óptima, ya que JOIN' ing tiene un problema de pérdida de memoria exponencial. Tome todos los datos, luego use un lenguaje de script del lado del servidor para resolverlos:

SELECT * FROM Address WHERE Personid IN(1,2,3);

Resultados:

Address.id : 1            // First person and their address
Address.Personid : 1
Address.City : "Boston"

Address.id : 2            // First person's second address
Address.Personid : 1
Address.City : "New York"

Address.id : 3            // Second person's address
Address.Personid : 2
Address.City : "Barcelona"

Aquí, obtengo todos los registros, en una declaración de selección. Es mejor que JOINobtener un pequeño grupo de estos registros, uno a la vez, como un subcomponente de otra consulta. Luego lo analizo con un código del lado del servidor que se parece a ...

<?php
    foreach($addresses as $address) {
         $persons[$address['Personid']]->Address[] = $address;
    }
?>

Cuándo no usar JOIN para la optimización

JOIN'formar un gran grupo de registros basado en una relación uno a uno con un solo registro produce una eficiencia óptima en comparación con múltiples SELECTdeclaraciones, una tras otra, que simplemente obtienen el siguiente tipo de registro.

Pero JOINes ineficiente cuando se obtienen registros con una relación de uno a muchos.

Ejemplo: la base de datos Blogs tiene 3 tablas de interés, Blogpost, Tag y Comment.

SELECT * from BlogPost
LEFT JOIN Tag ON Tag.BlogPostid = BlogPost.id
LEFT JOIN Comment ON Comment.BlogPostid = BlogPost.id;

Si hay 1 blogpost, 2 etiquetas y 2 comentarios, obtendrá resultados como:

Row1: tag1, comment1,
Row2: tag1, comment2,
Row3: tag2, comment1,
Row4: tag2, comment2,

Observe cómo se duplica cada registro. Bien, entonces, 2 comentarios y 2 etiquetas son 4 filas. ¿Qué pasa si tenemos 4 comentarios y 4 etiquetas? No obtienes 8 filas, obtienes 16 filas:

Row1: tag1, comment1,
Row2: tag1, comment2,
Row3: tag1, comment3,
Row4: tag1, comment4,
Row5: tag2, comment1,
Row6: tag2, comment2,
Row7: tag2, comment3,
Row8: tag2, comment4,
Row9: tag3, comment1,
Row10: tag3, comment2,
Row11: tag3, comment3,
Row12: tag3, comment4,
Row13: tag4, comment1,
Row14: tag4, comment2,
Row15: tag4, comment3,
Row16: tag4, comment4,

Agregue más tablas, más registros, etc., y el problema se inflará rápidamente a cientos de filas que están llenas de datos en su mayoría redundantes.

¿Cuánto te cuestan estos duplicados? Memoria (en el servidor SQL y el código que intenta eliminar los duplicados) y recursos de red (entre el servidor SQL y su servidor de código).

Fuente: https://dev.mysql.com/doc/refman/8.0/en/nested-join-optimization.html ; https://dev.mysql.com/doc/workbench/en/wb-relationship-tools.html


Te pierdes el punto. No se trata de uno a (uno | muchos). Se trata de si los conjuntos de filas tienen sentido si se combinan entre sí. Estás pidiendo dos conjuntos de datos relacionados tangencialmente. Si estaba pidiendo comentarios y, digamos, la información de contacto de sus autores, eso tiene más sentido como una unión, a pesar de que la gente presumiblemente puede escribir más de un comentario.
cHao

@ cHao: Gracias por tu comentario. Mi respuesta anterior es un resumen de la documentación de MySQL encontrar aquí: dev.mysql.com/doc/workbench/en/wb-relationship-tools.html
HoldOffHunger

Esa no es la documentación de MySQL. Es documentación para una herramienta GUI particular para trabajar con bases de datos MySQL. Y no ofrece ninguna guía sobre cuándo las uniones son (o no) apropiadas.
cHao

@ cHao: Lo siento, quise decir la documentación de MySQL (R) para MySQL WorkBench (TM), no MySQL Server (TM).
HoldOffHunger

Dejando a un lado la pedantería, la relevancia no está clara. Ambos mencionan las relaciones uno a uno y uno a muchos, pero ahí es donde termina la comunidad. De cualquier manera, el problema es sobre la relación entre los conjuntos de datos. Únete a dos conjuntos no relacionados, obtendrás todas las combinaciones de los dos. Divida los datos relacionados en múltiples selecciones, y ahora ha realizado múltiples consultas para dudoso beneficio, y comenzó a hacer el trabajo de MySQL para ello.
cHao

8

Construya consultas y uniones separadas, luego cronometre cada una de ellas; nada ayuda más que los números del mundo real.

Entonces, aún mejor: agregue "EXPLICAR" al comienzo de cada consulta. Esto le indicará cuántas subconsultas está utilizando MySQL para responder a su solicitud de datos y cuántas filas escaneadas para cada consulta.


7

Dependiendo de la complejidad de la base de datos en comparación con la complejidad del desarrollador, puede ser más sencillo hacer muchas llamadas SELECT.

Intente ejecutar algunas estadísticas de la base de datos contra JOIN y SELECTS múltiples. Vea si en su entorno JOIN es más rápido / lento que SELECT.

Por otra parte, si cambiarlo a JOIN significaría un día / semana / mes adicional de trabajo de desarrollo, me quedaría con varios SELECT

Salud,

BLT


5

En mi experiencia, descubrí que generalmente es más rápido ejecutar varias consultas, especialmente al recuperar grandes conjuntos de datos.

Al interactuar con la base de datos desde otra aplicación, como PHP, existe el argumento de un viaje al servidor sobre muchos.

Hay otras formas de limitar la cantidad de viajes realizados al servidor y aún ejecutar múltiples consultas que a menudo no solo son más rápidas sino que también hacen que la aplicación sea más fácil de leer, por ejemplo mysqli_multi_query.

No soy un novato cuando se trata de SQL, creo que hay una tendencia para los desarrolladores, especialmente los juniors, a pasar mucho tiempo tratando de escribir uniones muy inteligentes porque se ven inteligentes, mientras que en realidad hay formas inteligentes de extraer datos que se ven sencillo.

El último párrafo fue una opinión personal, pero espero que esto ayude. Sin embargo, estoy de acuerdo con los demás, que dicen que debe comparar. Ningún enfoque es una bala de plata.


Sí, también debemos tener en cuenta no solo las consultas en sí, sino también el procesamiento de datos dentro de la aplicación. Si busca datos con uniones externas, hay una cierta redundancia (a veces puede ser realmente enorme) que debe ser ordenada por la aplicación (generalmente en alguna biblioteca ORM), por lo tanto, en resumen, la única consulta SELECT with JOIN podría consumir más CPU y tiempo de dos SELECTs simples
JustAMartin

4

Si debe usar una combinación es ante todo si una combinación tiene sentido . Solo en ese punto es el rendimiento incluso algo a tener en cuenta, ya que casi todos los demás casos darán lugar a un rendimiento significativamente peor .

Las diferencias de rendimiento dependerán en gran medida de cuán relacionada esté la información que está consultando. Se une al trabajo, y son rápidos cuando los datos están relacionados y se indexan correctamente, pero a menudo generan redundancia y, a veces, más resultados de los necesarios. Y si sus conjuntos de datos no están directamente relacionados, pegarlos en una sola consulta dará como resultado lo que se llama un producto cartesiano (básicamente, todas las combinaciones posibles de filas), que casi nunca es lo que desea.

Esto a menudo es causado por relaciones de muchos a uno a muchos. Por ejemplo, la respuesta de HoldOffHunger mencionó una sola consulta para publicaciones, etiquetas y comentarios. Los comentarios están relacionados con una publicación, al igual que las etiquetas ... pero las etiquetas no están relacionadas con los comentarios.

+------------+     +---------+     +---------+
|  comment   |     |   post  |     |  tag    |
|------------|*   1|---------|1   *|---------|
| post_id    |-----| post_id |-----| post_id |
| comment_id |     | ...     |     | tag_id  |
| user_id    |     |         |     | ...     |
| ...        |     |         |     | ...     |
+------------+     +---------+     +---------+

En este caso, es inequívocamente mejor que esto sea al menos dos consultas separadas. Si intenta unir etiquetas y comentarios, porque no hay una relación directa entre los dos, terminará con todas las combinaciones posibles de etiqueta y comentario. many * many == manymany. Aparte de eso, dado que las publicaciones y las etiquetas no están relacionadas, puede hacer esas dos consultas en paralelo, lo que lleva a una ganancia potencial.

Sin embargo, consideremos un escenario diferente: desea los comentarios adjuntos a una publicación y la información de contacto de los comentaristas.

 +----------+     +------------+     +---------+
 |   user   |     |  comment   |     |   post  |
 |----------|1   *|------------|*   1|---------|
 | user_id  |-----| post_id    |-----| post_id |
 | username |     | user_id    |     | ...     |
 | ...      |     | ...        |     +---------+
 +----------+     +------------+

Aquí es donde debes considerar unirte. Además de ser una consulta mucho más natural, la mayoría de los sistemas de bases de datos (incluido MySQL) tienen muchas personas inteligentes que realizan mucho trabajo duro para optimizar consultas como esta. Para consultas separadas, dado que cada consulta depende de los resultados de la anterior, las consultas no se pueden hacer en paralelo, y el tiempo total se convierte no solo en el tiempo de ejecución real de las consultas, sino también en el tiempo dedicado a buscar resultados, tamizar a través de ellos para obtener ID para la siguiente consulta, vincular filas, etc.


Si recupera muchas columnas de usuario en el segundo escenario (y los mismos usuarios comentan más de una vez), esto deja abierta la pregunta de si es mejor recuperarlas en una consulta separada.
Adrian Baker,

@AdrianBaker: Como dije, muchas personas inteligentes están trabajando mucho. Si fuera a optimizar mi servidor SQL, mi primera idea sería usar la compresión, lo que eliminaría una gran cantidad de redundancia sin cambiar el código mucho en absoluto. Las optimizaciones del siguiente nivel incluirían reorganizar el resultado en tablas y enviarlas junto con tuplas de identificadores de fila, que la biblioteca del cliente podría ensamblar fácilmente de lado según sea necesario.
cHao

Ambas optimizaciones podrían hacer maravillas con una combinación para reducir o incluso eliminar la redundancia, pero no hay mucho que pueda ayudar con las consultas inherentemente en serie que tendrías que hacer para buscar registros relacionados.
cHao

3

¿Será más rápido en términos de rendimiento? Probablemente. Pero también potencialmente bloquea más objetos de base de datos a la vez (dependiendo de su base de datos y su esquema) y, por lo tanto, disminuye la concurrencia. En mi experiencia, la gente a menudo se confunde con el argumento de "menos viajes de ida y vuelta a la base de datos" cuando en realidad en la mayoría de los sistemas OLTP donde la base de datos está en la misma LAN, el cuello de botella real rara vez es la red.



1

Hay varios factores que significa que no hay una respuesta binaria. La cuestión de qué es lo mejor para el rendimiento depende de su entorno. Por cierto, si su selección única con un identificador no es inferior a un segundo, algo puede estar mal con su configuración.

La verdadera pregunta que debe hacerse es cómo desea acceder a los datos. Single selecciona soporte de enlace tardío. Por ejemplo, si solo desea información de los empleados, puede seleccionar de la tabla Empleados. Las relaciones de clave externa se pueden utilizar para recuperar recursos relacionados en un momento posterior y según sea necesario. Las selecciones ya tendrán una clave para señalar, por lo que deben ser extremadamente rápidas, y solo tiene que recuperar lo que necesita. La latencia de la red siempre debe tenerse en cuenta.

Las uniones recuperarán todos los datos a la vez. Si está generando un informe o completando una cuadrícula, esto puede ser exactamente lo que desea. Las uniones compiladas y optimizadas simplemente serán más rápidas que las selecciones individuales en este escenario. Recuerde, las uniones ad-hoc pueden no ser tan rápidas: debe compilarlas (en un proceso almacenado). La respuesta rápida depende del plan de ejecución, que detalla exactamente qué pasos toma el DBMS para recuperar los datos.


0

Sí, una consulta usando JOINS sería más rápida. Aunque sin conocer las relaciones de las tablas que está consultando, el tamaño de su conjunto de datos o dónde están las claves principales, es casi imposible decir cuánto más rápido.

¿Por qué no probar ambos escenarios? Entonces sabrás con seguridad ...

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.