Unirse frente a subconsulta

838

Soy un usuario de MySQL de la vieja escuela y siempre he preferido JOINla subconsulta. Pero hoy en día todos usan subconsultas, y lo odio; No se porque.

Me falta el conocimiento teórico para juzgar por mí mismo si hay alguna diferencia. ¿Es una subconsulta tan buena como a JOINy, por lo tanto, no hay nada de qué preocuparse?

Su sentido común
fuente
23
Las subconsultas son geniales a veces. Apestan en términos de rendimiento en MySQL. No los uses.
runrig
8
Siempre tuve la impresión de que las subconsultas se ejecutaban implícitamente como uniones donde estaban disponibles en ciertas tecnologías de base de datos.
Kezzer
18
Las subconsultas no siempre son malas, cuando se unen con tablas bastante grandes, la forma preferida es hacer una sub-selección de esa tabla grande (limitando el número de filas) y luego unirlas.
ovais.tariq
136
"hoy en día todos usan
subconsultas
3
Potencialmente relacionado (aunque mucho más específico): stackoverflow.com/questions/141278/subqueries-vs-joins/…
Leigh Brenecki

Respuestas:

191

Tomado del manual de MySQL ( 13.2.10.11 Reescritura de subconsultas como uniones ):

Una IZQUIERDA [EXTERIOR] IZQUIERDA puede ser más rápida que una subconsulta equivalente porque el servidor podría optimizarla mejor, un hecho que no es específico solo del servidor MySQL.

Por lo tanto, las subconsultas pueden ser más lentas que LEFT [OUTER] JOIN, pero en mi opinión, su fuerza es una legibilidad ligeramente mayor.

simhumileco
fuente
45
@ user1735921 IMO depende ... En general, es muy importante la legibilidad del código, porque es de gran importancia para su posterior administración ... Recordemos la famosa declaración de Donald Knuth: "La optimización prematura es la raíz de todo maldad (o al menos la mayor parte) en programación " . Sin embargo, naturalmente, hay áreas de programación donde el rendimiento es primordial ... Idealmente, cuando uno logra conciliar uno con el otro :)
simhumileco
31
En consultas más complejas, encuentro que las uniones son mucho más fáciles de leer que las subconsultas. Las subconsultas se convierten en un cuenco de fideos en mi cabeza.
Zahra
66
@ user1735921 seguro, especialmente cuando la consulta se vuelve tan complicada que hace lo incorrecto y pasas un día arreglándola ... hay un equilibrio en el medio, como siempre.
fabio.sussetto
66
@ user1735921 Solo si las ganancias de rendimiento valen el aumento en el tiempo de mantenimiento requerido en el futuro
Joshua Schlichting
3
Mi opinión Joiny sub querytiene una sintaxis diferente, por lo que no se puede comparar la legibilidad, ambas tienen una mayor legibilidad siempre que sea bueno en la sintaxis SQL. El rendimiento es más importante.
Thavaprakash Swaminathan
843

Las subconsultas son la forma lógica correcta de resolver problemas de la forma "Obtener hechos de A, condicional a hechos de B". En tales casos, tiene más sentido lógico pegar B en una subconsulta que hacer una unión. También es más seguro, en un sentido práctico, ya que no tiene que ser cauteloso para obtener datos duplicados de A debido a múltiples partidos contra B.

En términos prácticos, sin embargo, la respuesta generalmente se reduce al rendimiento. Algunos optimizadores chupan limones cuando se les da una combinación frente a una subconsulta, y algunos chupan limones de la otra manera, y esto es específico del optimizador, específico de la versión DBMS y específico de la consulta.

Históricamente, las uniones explícitas generalmente ganan, de ahí la sabiduría establecida de que las uniones son mejores, pero los optimizadores mejoran todo el tiempo, por lo que prefiero escribir consultas primero de una manera lógicamente coherente, y luego reestructurarlas si las restricciones de rendimiento lo justifican.

Marcelo Cantos
fuente
105
Gran respuesta. También agregaría que los desarrolladores (especialmente los aficionados) no siempre dominan SQL.
Álvaro González
44
+1 Buscando una explicación lógica para este problema durante mucho tiempo, esta es solo la respuesta que me parece lógica
Ali Umair
1
@Marcelo Cantos, ¿podría dar un ejemplo de su afirmación "También es más seguro, en un sentido práctico, ya que no tiene que ser cauteloso para obtener datos duplicados de A debido a múltiples coincidencias contra B"? Esto me pareció muy perspicaz pero demasiado abstracto. Gracias.
Jinghui Niu
66
@JinghuiNiu Los clientes que compraron artículos caros: select custid from cust join bought using (custid) where price > 500. Si un cliente compró varios artículos caros, obtendrá duplicaciones. Para solucionar este problema, select custid from cust where exists (select * from bought where custid = cust.custid and price > 500). En su select distinct …lugar, podría usar , pero a menudo es más trabajo, ya sea para el optimizador o el evaluador.
Marcelo Cantos
1
@MatTheWhale sí, utilicé una respuesta demasiado simplificada porque era vago. En un escenario real, estaría sacando más columnas que solo custid de cust.
Marcelo Cantos
357

En la mayoría de los casos, los JOINs son más rápidos que las subconsultas y es muy raro que una subconsulta sea más rápida.

En JOINs RDBMS puede crear un plan de ejecución que sea mejor para su consulta y pueda predecir qué datos se deben cargar para procesarlos y ahorrar tiempo, a diferencia de la subconsulta donde ejecutará todas las consultas y cargará todos sus datos para realizar el procesamiento .

Lo bueno de las subconsultas es que son más legibles que JOINs: es por eso que la mayoría de las personas SQL nuevas las prefieren; es el camino facil; pero cuando se trata de rendimiento, las UNIONES son mejores en la mayoría de los casos, aunque no son difíciles de leer también.

Kronass
fuente
14
Sí, por lo tanto, la mayoría de las bases de datos lo incluyen como un paso de optimización para convertir subconsultas en combinaciones cuando analiza su consulta.
Cine
16
Esta respuesta está demasiado simplificada para la pregunta que se hizo. Como usted dice: ciertas subconsultas están bien y otras no. La respuesta realmente no ayuda a distinguir los dos. (también lo 'muy raro' realmente depende de sus datos / aplicación).
razón
21
¿Puedes probar alguno de tus puntos con referencias de documentación o resultados de pruebas?
Uğur Gümüşhan
62
Hice muy buenas experiencias con subconsultas que contienen una referencia a la consulta superior, especialmente cuando se trata de conteos de filas superiores a 100,000. La cosa parece ser el uso de la memoria y la paginación al archivo de intercambio. Una unión produciría una gran cantidad de datos, que pueden no caber en la memoria y deben paginarse en el archivo de intercambio. Siempre que este sea el caso, los tiempos de consulta de pequeñas sub-selecciones select * from a where a.x = (select b.x form b where b.id = a.id)son extremadamente pequeños en comparación con una unión. Este es un problema muy específico, pero en algunos casos te lleva de horas a minutos.
zuloo
13
Tengo experiencia con Oracle y puedo decir que las subconsultas son mucho mejores en tablas grandes si no tiene ningún filtro u ordenación en ellas.
Amir Pashazadeh
130

Use EXPLAIN para ver cómo su base de datos ejecuta la consulta en sus datos. Hay un gran "depende" en esta respuesta ...

PostgreSQL puede reescribir una subconsulta a una combinación o una combinación a una subconsulta cuando cree que una es más rápida que la otra. Todo depende de los datos, índices, correlación, cantidad de datos, consulta, etc.

Frank Heikens
fuente
66
esta es exactamente la razón por la cual postgresql es tan bueno y útil que comprende cuál es el objetivo y solucionará una consulta en función de lo que cree que es mejor y postgresql es muy bueno para saber cómo ver sus datos
WojonsTech
heww. ¡Supongo que no hay necesidad de volver a escribir toneladas de consultas para mí! postgresql para la victoria.
Daniel Shin
77

En el año 2010 me habría unido al autor de estas preguntas y habría votado fuertemente JOIN, pero con mucha más experiencia (especialmente en MySQL) puedo decir: Sí, las subconsultas pueden ser mejores. He leído múltiples respuestas aquí; Algunas subconsultas indicadas son más rápidas, pero carecía de una buena explicación. Espero poder brindarle una (muy) tardía respuesta:

En primer lugar, permítanme decir lo más importante: existen diferentes formas de subconsultas

Y la segunda declaración importante: el tamaño importa

Si usa subconsultas, debe saber cómo el DB-Server ejecuta la subconsulta. ¡Especialmente si la subconsulta se evalúa una vez o por cada fila! Por otro lado, un servidor DB moderno puede optimizar mucho. En algunos casos, una subconsulta ayuda a optimizar una consulta, pero una versión más reciente del servidor DB puede hacer que la optimización sea obsoleta.

Subconsultas en campos de selección

SELECT moo, (SELECT roger FROM wilco WHERE moo = me) AS bar FROM foo

Tenga en cuenta que se ejecuta una subconsulta para cada fila resultante de foo.
Evite esto si es posible; puede ralentizar drásticamente su consulta en grandes conjuntos de datos. Sin embargo, si la subconsulta no tiene ninguna referencia foo, el servidor DB puede optimizarla como contenido estático y solo se puede evaluar una vez.

Subconsultas en la instrucción Where

SELECT moo FROM foo WHERE bar = (SELECT roger FROM wilco WHERE moo = me)

Si tiene suerte, el DB optimiza esto internamente en a JOIN. De lo contrario, su consulta se volverá muy, muy lenta en grandes conjuntos de datos porque ejecutará la subconsulta para cada fila foo, no solo los resultados como en el tipo de selección.

Subconsultas en la declaración de unión

SELECT moo, bar 
  FROM foo 
    LEFT JOIN (
      SELECT MIN(bar), me FROM wilco GROUP BY me
    ) ON moo = me

Esto es interesante. Combinamos JOINcon una subconsulta. Y aquí obtenemos la verdadera fuerza de las subconsultas. Imagine un conjunto de datos con millones de filas wilcopero solo unas pocas distintas me. En lugar de unirse contra una mesa enorme, ahora tenemos una mesa temporal más pequeña para unirse. Esto puede resultar en consultas mucho más rápidas dependiendo del tamaño de la base de datos. Puede tener el mismo efecto con CREATE TEMPORARY TABLE ...y INSERT INTO ... SELECT ..., lo que podría proporcionar una mejor legibilidad en consultas muy complejas (pero puede bloquear conjuntos de datos en un nivel de aislamiento de lectura repetible).

Subconsultas anidadas

SELECT moo, bar
  FROM (
    SELECT moo, CONCAT(roger, wilco) AS bar
      FROM foo
      GROUP BY moo
      HAVING bar LIKE 'SpaceQ%'
  ) AS temp_foo
  ORDER BY bar

Puede anidar subconsultas en varios niveles. Esto puede ayudar en grandes conjuntos de datos si tiene que agrupar u ordenar los resultados. Por lo general, el servidor DB crea una tabla temporal para esto, pero a veces no es necesario ordenar en toda la tabla, solo en el conjunto de resultados. Esto podría proporcionar un rendimiento mucho mejor dependiendo del tamaño de la tabla.

Conclusión

Las subconsultas no reemplazan a a JOINy no debe usarlas así (aunque sea posible). En mi humilde opinión, el uso correcto de una subconsulta es el uso como un reemplazo rápido de CREATE TEMPORARY TABLE .... Una buena subconsulta reduce un conjunto de datos de una manera que no puede lograr en una ONdeclaración de a JOIN. Si una subconsulta tiene una de las palabras clave GROUP BYo, DISTINCTy preferiblemente no está situada en los campos de selección o en la instrucción where, podría mejorar mucho el rendimiento.

Trendfischer
fuente
3
Para Sub-queries in the Join-statement: (1) generar una tabla derivada a partir de la subconsulta en sí podría tomar mucho tiempo. (2) la tabla derivada resultante no está indexada. estos dos solos podrían ralentizar significativamente el SQL.
jxc
@jxc Solo puedo hablar por MySQL (1) Allí hay una tabla temporal similar a una unión. El tiempo depende de la cantidad de datos. Si no puede reducir los datos con una subconsulta, use una unión. (2) Esto es correcto, depende del factor que puede reducir los datos en la tabla temporal. Tuve casos del mundo real, donde pude reducir el tamaño de la unión de algunos millones a unos cientos y reducir el tiempo de consulta de varios segundos (con el uso del índice completo) a un cuarto de segundo con una subconsulta.
Trendfischer
OMI: (1) dicha tabla temporal (tabla derivada) no se materializa, por lo tanto, cada vez que ejecuta el SQL, debe recrearse la tabla temporal, que podría ser muy costosa y un verdadero cuello de botella (es decir, ejecutar un grupo por millones) de registros) (2) incluso si puede reducir el tamaño de la tabla temporal a 10registros, ya que no hay índice, eso significa potencialmente consultar 9 veces más registros de datos que sin la tabla temporal cuando se UNE a otras tablas. Por cierto, tuve este problema antes con mi db (MySQL), en mi caso, usar subconsultas SELECT listpodría ser mucho más rápido.
jxc
@jxc No dudo que haya muchos ejemplos en los que usar una subconsulta es menos óptimo. Como buena práctica, debe usar EXPLAINen una consulta antes de optimizar. Con el viejo set profiling=1se podía ver fácilmente, si una tabla temporal es un cuello de botella. E incluso un índice necesita tiempo de procesamiento, B-Trees optimiza la consulta de registros, pero una tabla de 10 registros puede ser mucho más rápida que un índice para millones de registros. Pero depende de múltiples factores como los tamaños y tipos de campo.
Trendfischer
1
Realmente disfruté tu explicación. Gracias.
unpairestgood
43

En primer lugar, para comparar los dos primeros, debe distinguir las consultas con subconsultas para:

  1. Una clase de subconsultas que siempre tienen una consulta equivalente correspondiente escrita con combinaciones
  2. Una clase de subconsultas que no se pueden reescribir usando combinaciones

Para la primera clase de consultas, un buen RDBMS verá uniones y subconsultas como equivalentes y producirá los mismos planes de consulta.

En estos días, incluso mysql hace eso.

Aún así, a veces no es así, pero esto no significa que las uniones siempre ganarán: tuve casos al usar subconsultas en mysql para mejorar el rendimiento. (Por ejemplo, si hay algo que impide que mysql planner calcule correctamente el costo y si el planificador no ve la variante de combinación y la variante de subconsulta como iguales, entonces las subconsultas pueden superar las uniones forzando una determinada ruta).

La conclusión es que debe probar sus consultas para las variantes de unión y subconsulta si desea asegurarse de cuál funcionará mejor.

Para la segunda clase, la comparación no tiene sentido ya que esas consultas no pueden reescribirse usando combinaciones y en estos casos las subconsultas son una forma natural de realizar las tareas requeridas y no debe discriminarlas.

Insensatez
fuente
1
¿puede proporcionar un ejemplo de una consulta escrita utilizando subconsultas que no se pueden convertir en combinaciones (segunda clase, como la llama)?
Zahra
24

Creo que lo que se ha subestimado en las respuestas citadas es la cuestión de los duplicados y los resultados problemáticos que pueden surgir de casos específicos (de uso).

(aunque Marcelo Cantos lo menciona)

Citaré el ejemplo de los cursos Lagunita de Stanford sobre SQL.

Mesa de estudiante

+------+--------+------+--------+
| sID  | sName  | GPA  | sizeHS |
+------+--------+------+--------+
|  123 | Amy    |  3.9 |   1000 |
|  234 | Bob    |  3.6 |   1500 |
|  345 | Craig  |  3.5 |    500 |
|  456 | Doris  |  3.9 |   1000 |
|  567 | Edward |  2.9 |   2000 |
|  678 | Fay    |  3.8 |    200 |
|  789 | Gary   |  3.4 |    800 |
|  987 | Helen  |  3.7 |    800 |
|  876 | Irene  |  3.9 |    400 |
|  765 | Jay    |  2.9 |   1500 |
|  654 | Amy    |  3.9 |   1000 |
|  543 | Craig  |  3.4 |   2000 |
+------+--------+------+--------+

Aplicar tabla

(solicitudes hechas a universidades y especialidades específicas)

+------+----------+----------------+----------+
| sID  | cName    | major          | decision |
+------+----------+----------------+----------+
|  123 | Stanford | CS             | Y        |
|  123 | Stanford | EE             | N        |
|  123 | Berkeley | CS             | Y        |
|  123 | Cornell  | EE             | Y        |
|  234 | Berkeley | biology        | N        |
|  345 | MIT      | bioengineering | Y        |
|  345 | Cornell  | bioengineering | N        |
|  345 | Cornell  | CS             | Y        |
|  345 | Cornell  | EE             | N        |
|  678 | Stanford | history        | Y        |
|  987 | Stanford | CS             | Y        |
|  987 | Berkeley | CS             | Y        |
|  876 | Stanford | CS             | N        |
|  876 | MIT      | biology        | Y        |
|  876 | MIT      | marine biology | N        |
|  765 | Stanford | history        | Y        |
|  765 | Cornell  | history        | N        |
|  765 | Cornell  | psychology     | Y        |
|  543 | MIT      | CS             | N        |
+------+----------+----------------+----------+

Tratemos de encontrar los puntajes de GPA para los estudiantes que se han postulado a la CSespecialización (independientemente de la universidad)

Usando una subconsulta:

select GPA from Student where sID in (select sID from Apply where major = 'CS');

+------+
| GPA  |
+------+
|  3.9 |
|  3.5 |
|  3.7 |
|  3.9 |
|  3.4 |
+------+

El valor promedio para este conjunto de resultados es:

select avg(GPA) from Student where sID in (select sID from Apply where major = 'CS');

+--------------------+
| avg(GPA)           |
+--------------------+
| 3.6800000000000006 |
+--------------------+

Usando una unión:

select GPA from Student, Apply where Student.sID = Apply.sID and Apply.major = 'CS';

+------+
| GPA  |
+------+
|  3.9 |
|  3.9 |
|  3.5 |
|  3.7 |
|  3.7 |
|  3.9 |
|  3.4 |
+------+

valor promedio para este conjunto de resultados:

select avg(GPA) from Student, Apply where Student.sID = Apply.sID and Apply.major = 'CS';

+-------------------+
| avg(GPA)          |
+-------------------+
| 3.714285714285714 |
+-------------------+

Es obvio que el segundo intento arroja resultados engañosos en nuestro caso de uso, dado que cuenta duplicados para el cálculo del valor promedio. También es evidente que el uso de distinctcon la declaración basada en unirse no eliminará el problema, dado que mantendrá erróneamente una de cada tres ocurrencias de la 3.9puntuación. El caso correcto es dar cuenta de DOS (2) ocurrencias del 3.9puntaje dado que en realidad tenemos DOS (2) estudiantes con ese puntaje que cumplen con nuestros criterios de consulta.

Parece que en algunos casos una subconsulta es la forma más segura de hacerlo, además de cualquier problema de rendimiento.

pkaramol
fuente
Creo que no puedes usar una subconsulta aquí. Este no es un caso en el que lógicamente puede usar cualquiera de los dos, pero uno da una respuesta incorrecta debido a la implementación técnica del mismo. Este es un caso en el que NO PUEDE usar una subconsulta porque un estudiante que no pertenece a CS puede obtener un puntaje de 3.9 que está en la lista de puntajes IN. El contexto de CS se pierde una vez que se ejecuta la subconsulta, que no es lo que queremos lógicamente. Por lo tanto, este no es un buen ejemplo en el que se pueda usar. El uso de la subconsulta es conceptual / lógicamente incorrecto para este caso de uso, incluso si, por suerte, da el resultado correcto para un conjunto de datos diferente.
Saurabh Patil
22

La documentación de MSDN para SQL Server dice

Muchas declaraciones Transact-SQL que incluyen subconsultas pueden formularse alternativamente como uniones. Otras preguntas se pueden plantear solo con subconsultas. En Transact-SQL, generalmente no hay diferencia de rendimiento entre una declaración que incluye una subconsulta y una versión semánticamente equivalente que no. Sin embargo, en algunos casos donde se debe verificar la existencia, una unión produce un mejor rendimiento. De lo contrario, la consulta anidada debe procesarse para cada resultado de la consulta externa para garantizar la eliminación de duplicados. En tales casos, un enfoque de combinación produciría mejores resultados.

así que si necesitas algo como

select * from t1 where exists select * from t2 where t2.parent=t1.id

intenta usar join en su lugar. En otros casos, no hay diferencia.

Digo: Crear funciones para subconsultas elimina el problema del desorden y le permite implementar lógica adicional a las subconsultas. Por lo tanto, recomiendo crear funciones para subconsultas siempre que sea posible.

El desorden en el código es un gran problema y la industria ha estado trabajando para evitarlo durante décadas.

Uğur Gümüşhan
fuente
99
Reemplazar subconsultas con funciones es una muy mala idea en cuanto al rendimiento en algunos RDBMS (por ejemplo, Oracle), por lo que recomendaría todo lo contrario: usar subconsultas / uniones en lugar de funciones siempre que sea posible.
Frank Schmitt
3
@FrankSchmitt apoya tu argumento con referencias.
Uğur Gümüşhan
2
También hay casos en los que debería usar una subconsulta en lugar de una combinación, incluso si verifica la existencia: si lo hace NOT EXISTS. A NOT EXISTSgana sobre a LEFT OUTER JOIN por varias razones: rendimiento, seguridad ante fallas (en el caso de columnas anulables) y legibilidad. sqlperformance.com/2012/12/t-sql-queries/left-anti-semi-join
Tim Schmelter
16

Ejecutar en una base de datos muy grande desde un viejo CMS Mambo:

SELECT id, alias
FROM
  mos_categories
WHERE
  id IN (
    SELECT
      DISTINCT catid
    FROM mos_content
  );

0 segundos

SELECT
  DISTINCT mos_content.catid,
  mos_categories.alias
FROM
  mos_content, mos_categories
WHERE
  mos_content.catid = mos_categories.id;

~ 3 segundos

Una EXPLICACIÓN muestra que examinan exactamente el mismo número de filas, pero una toma 3 segundos y la otra es casi instantánea. ¿Moraleja de la historia? Si el rendimiento es importante (¿cuándo no lo es?), Pruébelo de varias maneras y vea cuál es el más rápido.

Y...

SELECT
  DISTINCT mos_categories.id,
  mos_categories.alias
FROM
  mos_content, mos_categories
WHERE
  mos_content.catid = mos_categories.id;

0 segundos

De nuevo, los mismos resultados, el mismo número de filas examinadas. Supongo que DISTINCT mos_content.catid tarda mucho más en darse cuenta que DISTINCT mos_categories.id.

Jason
fuente
1
Me gustaría saber más acerca de lo que está intentando señalar en la última línea "Supongo que DISTINCT mos_content.catid tarda mucho más en darse cuenta que DISTINCT mos_categories.id". . ¿Estás diciendo que una identificación debe ser nombrada solo idy no nombrada algo así catid? Tratando de optimizar mis accesos db, y sus aprendizajes podrían ayudar.
bool.dev
2
usar SQL IN en ese caso es una mala práctica y no prueba nada.
Uğur Gümüşhan
15

Según mi observación, como dos casos, si una tabla tiene menos de 100,000 registros, la unión funcionará rápidamente.

Pero en el caso de que una tabla tenga más de 100,000 registros, entonces una subconsulta es el mejor resultado.

Tengo una tabla que tiene 500,000 registros que creé debajo de la consulta y su tiempo de resultado es como

SELECT * 
FROM crv.workorder_details wd 
inner join  crv.workorder wr on wr.workorder_id = wd.workorder_id;

Resultado: 13.3 Segundos

select * 
from crv.workorder_details 
where workorder_id in (select workorder_id from crv.workorder)

Resultado: 1.65 Segundos

Vijay Gajera
fuente
Estoy de acuerdo, a veces, romper la consulta también funciona, cuando tienes millones de registros, no quieres usar combinaciones porque toman para siempre. Más bien manejarlo en el código y el mapa en el código es mejor.
user1735921
1
Ate sus uniones no funcionan lo suficientemente rápido, es posible que le falte un índice. El Analizador de consultas puede ser bastante útil para comparar el rendimiento real.
digital.aaron
Estoy de acuerdo con Ajay Gajera, lo he visto por mí mismo.
user1735921
14
¿Cómo tiene sentido comparar el rendimiento de dos consultas que arrojan resultados diferentes?
Paul Spiegel
Sí, esas son consultas diferentes pero devuelven el mismo resultado
king neo
12

Las subconsultas generalmente se usan para devolver una sola fila como valor atómico, aunque se pueden usar para comparar valores contra varias filas con la palabra clave IN. Se permiten en casi cualquier punto significativo en una declaración SQL, incluida la lista de destino, la cláusula WHERE, etc. Una subconsulta simple podría usarse como condición de búsqueda. Por ejemplo, entre un par de tablas:

   SELECT title FROM books WHERE author_id = (SELECT id FROM authors WHERE last_name = 'Bar' AND first_name = 'Foo');

Tenga en cuenta que el uso de un operador de valor normal en los resultados de una subconsulta requiere que solo se devuelva un campo. Si está interesado en verificar la existencia de un solo valor dentro de un conjunto de otros valores, use IN:

   SELECT title FROM books WHERE author_id IN (SELECT id FROM authors WHERE last_name ~ '^[A-E]');

Obviamente, esto es diferente de decir una IZQUIERDA IZQUIERDA donde solo desea unir elementos de la tabla A y B, incluso si la condición de unión no encuentra ningún registro coincidente en la tabla B, etc.

Si solo le preocupa la velocidad, tendrá que consultar con su base de datos y escribir una buena consulta y ver si hay alguna diferencia significativa en el rendimiento.

rkulla
fuente
11

Versión de MySQL: 5.5.28-0ubuntu0.12.04.2-log

También tenía la impresión de que JOIN siempre es mejor que una subconsulta en MySQL, pero EXPLAIN es una mejor manera de hacer un juicio. Aquí hay un ejemplo donde las subconsultas funcionan mejor que JOIN.

Aquí está mi consulta con 3 subconsultas:

EXPLAIN SELECT vrl.list_id,vrl.ontology_id,vrl.position,l.name AS list_name, vrlih.position AS previous_position, vrl.moved_date 
FROM `vote-ranked-listory` vrl 
INNER JOIN lists l ON l.list_id = vrl.list_id 
INNER JOIN `vote-ranked-list-item-history` vrlih ON vrl.list_id = vrlih.list_id AND vrl.ontology_id=vrlih.ontology_id AND vrlih.type='PREVIOUS_POSITION' 
INNER JOIN list_burial_state lbs ON lbs.list_id = vrl.list_id AND lbs.burial_score < 0.5 
WHERE vrl.position <= 15 AND l.status='ACTIVE' AND l.is_public=1 AND vrl.ontology_id < 1000000000 
 AND (SELECT list_id FROM list_tag WHERE list_id=l.list_id AND tag_id=43) IS NULL 
 AND (SELECT list_id FROM list_tag WHERE list_id=l.list_id AND tag_id=55) IS NULL 
 AND (SELECT list_id FROM list_tag WHERE list_id=l.list_id AND tag_id=246403) IS NOT NULL 
ORDER BY vrl.moved_date DESC LIMIT 200;

EXPLICAR muestra:

+----+--------------------+----------+--------+-----------------------------------------------------+--------------+---------+-------------------------------------------------+------+--------------------------+
| id | select_type        | table    | type   | possible_keys                                       | key          | key_len | ref                                             | rows | Extra                    |
+----+--------------------+----------+--------+-----------------------------------------------------+--------------+---------+-------------------------------------------------+------+--------------------------+
|  1 | PRIMARY            | vrl      | index  | PRIMARY                                             | moved_date   | 8       | NULL                                            |  200 | Using where              |
|  1 | PRIMARY            | l        | eq_ref | PRIMARY,status,ispublic,idx_lookup,is_public_status | PRIMARY      | 4       | ranker.vrl.list_id                              |    1 | Using where              |
|  1 | PRIMARY            | vrlih    | eq_ref | PRIMARY                                             | PRIMARY      | 9       | ranker.vrl.list_id,ranker.vrl.ontology_id,const |    1 | Using where              |
|  1 | PRIMARY            | lbs      | eq_ref | PRIMARY,idx_list_burial_state,burial_score          | PRIMARY      | 4       | ranker.vrl.list_id                              |    1 | Using where              |
|  4 | DEPENDENT SUBQUERY | list_tag | ref    | list_tag_key,list_id,tag_id                         | list_tag_key | 9       | ranker.l.list_id,const                          |    1 | Using where; Using index |
|  3 | DEPENDENT SUBQUERY | list_tag | ref    | list_tag_key,list_id,tag_id                         | list_tag_key | 9       | ranker.l.list_id,const                          |    1 | Using where; Using index |
|  2 | DEPENDENT SUBQUERY | list_tag | ref    | list_tag_key,list_id,tag_id                         | list_tag_key | 9       | ranker.l.list_id,const                          |    1 | Using where; Using index |
+----+--------------------+----------+--------+-----------------------------------------------------+--------------+---------+-------------------------------------------------+------+--------------------------+

La misma consulta con JOIN es:

EXPLAIN SELECT vrl.list_id,vrl.ontology_id,vrl.position,l.name AS list_name, vrlih.position AS previous_position, vrl.moved_date 
FROM `vote-ranked-listory` vrl 
INNER JOIN lists l ON l.list_id = vrl.list_id 
INNER JOIN `vote-ranked-list-item-history` vrlih ON vrl.list_id = vrlih.list_id AND vrl.ontology_id=vrlih.ontology_id AND vrlih.type='PREVIOUS_POSITION' 
INNER JOIN list_burial_state lbs ON lbs.list_id = vrl.list_id AND lbs.burial_score < 0.5 
LEFT JOIN list_tag lt1 ON lt1.list_id = vrl.list_id AND lt1.tag_id = 43 
LEFT JOIN list_tag lt2 ON lt2.list_id = vrl.list_id AND lt2.tag_id = 55 
INNER JOIN list_tag lt3 ON lt3.list_id = vrl.list_id AND lt3.tag_id = 246403 
WHERE vrl.position <= 15 AND l.status='ACTIVE' AND l.is_public=1 AND vrl.ontology_id < 1000000000 
AND lt1.list_id IS NULL AND lt2.tag_id IS NULL 
ORDER BY vrl.moved_date DESC LIMIT 200;

y la salida es:

+----+-------------+-------+--------+-----------------------------------------------------+--------------+---------+---------------------------------------------+------+----------------------------------------------+
| id | select_type | table | type   | possible_keys                                       | key          | key_len | ref                                         | rows | Extra                                        |
+----+-------------+-------+--------+-----------------------------------------------------+--------------+---------+---------------------------------------------+------+----------------------------------------------+
|  1 | SIMPLE      | lt3   | ref    | list_tag_key,list_id,tag_id                         | tag_id       | 5       | const                                       | 2386 | Using where; Using temporary; Using filesort |
|  1 | SIMPLE      | l     | eq_ref | PRIMARY,status,ispublic,idx_lookup,is_public_status | PRIMARY      | 4       | ranker.lt3.list_id                          |    1 | Using where                                  |
|  1 | SIMPLE      | vrlih | ref    | PRIMARY                                             | PRIMARY      | 4       | ranker.lt3.list_id                          |  103 | Using where                                  |
|  1 | SIMPLE      | vrl   | ref    | PRIMARY                                             | PRIMARY      | 8       | ranker.lt3.list_id,ranker.vrlih.ontology_id |   65 | Using where                                  |
|  1 | SIMPLE      | lt1   | ref    | list_tag_key,list_id,tag_id                         | list_tag_key | 9       | ranker.lt3.list_id,const                    |    1 | Using where; Using index; Not exists         |
|  1 | SIMPLE      | lbs   | eq_ref | PRIMARY,idx_list_burial_state,burial_score          | PRIMARY      | 4       | ranker.vrl.list_id                          |    1 | Using where                                  |
|  1 | SIMPLE      | lt2   | ref    | list_tag_key,list_id,tag_id                         | list_tag_key | 9       | ranker.lt3.list_id,const                    |    1 | Using where; Using index                     |
+----+-------------+-------+--------+-----------------------------------------------------+--------------+---------+---------------------------------------------+------+----------------------------------------------+

Una comparación de la rowscolumna indica la diferencia y la consulta con JOIN está utilizando Using temporary; Using filesort.

Por supuesto, cuando ejecuto ambas consultas, la primera se realiza en 0.02 segundos, la segunda no se completa incluso después de 1 minuto, por lo que EXPLICAR explicó estas consultas correctamente.

Si no tengo el INNER JOIN en la list_tagtabla, es decir, si elimino

AND (SELECT list_id FROM list_tag WHERE list_id=l.list_id AND tag_id=246403) IS NOT NULL  

desde la primera consulta y correspondientemente:

INNER JOIN list_tag lt3 ON lt3.list_id = vrl.list_id AND lt3.tag_id = 246403

desde la segunda consulta, EXPLAIN devuelve el mismo número de filas para ambas consultas y ambas consultas se ejecutan igualmente rápido.

arun
fuente
Tengo situación similar, pero con más combinaciones que la suya, a tratar de explicar con una vez
pahnin
En Oracle o PostgreSQL hubiera intentado: Y NO EXISTE (SELECCIONE 1 de list_tag WHERE list_id = l.list_id AND tag_id en (43, 55, 246403))
David Aldridge
11

Las subconsultas tienen la capacidad de calcular funciones de agregación sobre una marcha. Por ejemplo, encuentre el precio mínimo del libro y obtenga todos los libros que se venden con este precio. 1) Uso de subconsultas:

SELECT titles, price
FROM Books, Orders
WHERE price = 
(SELECT MIN(price)
 FROM Orders) AND (Books.ID=Orders.ID);

2) usando JOINs

SELECT MIN(price)
     FROM Orders;
-----------------
2.99

SELECT titles, price
FROM Books b
INNER JOIN  Orders o
ON b.ID = o.ID
WHERE o.price = 2.99;
Vlad
fuente
Otro caso: múltiples GROUP BYs con diferentes tablas: stackoverflow.com/questions/11415284/… Las subconsultas parecen ser estrictamente más generales. Consulte también el hombre de MySQL: dev.mysql.com/doc/refman/5.7/en/optimizing-subqueries.html | dev.mysql.com/doc/refman/5.7/en/rewriting-subqueries.html
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
66
-1 Esto es engañoso ya que está utilizando una subconsulta y se une en ambos ejemplos. El hecho de que haya extraído la subconsulta en una segunda consulta para determinar el precio de pedido más bajo no tiene ningún efecto ya que la base de datos hará exactamente lo mismo. Además, no está reescribiendo la unión usando una subconsulta; ambas consultas usan una combinación. Usted está correcto que subconsultas permiten funciones de agregación, pero este ejemplo no demuestra ese hecho.
David Harkness
Estoy de acuerdo con David, y puedes usar group by para obtener el precio mínimo.
user1735921
9
  • Una regla general es que las uniones son más rápidas en la mayoría de los casos (99%).
  • Cuantas más tablas de datos tengan, las subconsultas son más lentas.
  • Cuantas menos tablas de datos tengan, las subconsultas tienen una velocidad equivalente a las uniones .
  • Las subconsultas son más simples, más fáciles de entender y más fáciles de leer.
  • La mayoría de los marcos web y de aplicaciones y sus "ORM" y "Registro activo" generan consultas con subconsultas , porque con las subconsultas es más fácil dividir la responsabilidad, mantener el código, etc.
  • Para sitios web más pequeños o aplicaciones, las subconsultas están bien, pero para sitios web y aplicaciones más grandes a menudo tendrá que reescribir consultas generadas para unir consultas, especialmente si una consulta usa muchas subconsultas en la consulta.

Algunas personas dicen que "algunos RDBMS pueden reescribir una subconsulta a una combinación o una combinación a una subconsulta cuando cree que una es más rápida que la otra", pero esta afirmación se aplica a casos simples, seguramente no para consultas complicadas con subconsultas que realmente causan una problemas en el rendimiento

fico7489
fuente
> pero esta declaración se aplica a casos simples. Entiendo que es un caso simple que RDBMS puede reescribir a "JOIN", o es un caso tan complejo que las subconsultas son apropiadas aquí. :-) Buen punto en ORMs. Creo que esto tiene el mayor impacto.
pilat
4

La diferencia solo se ve cuando la segunda tabla de unión tiene significativamente más datos que la tabla primaria. Tuve una experiencia como la de abajo ...

Teníamos una tabla de usuarios de cien mil entradas y sus datos de membresía (amistad) de aproximadamente trescientas mil entradas. Fue una declaración conjunta para tomar amigos y sus datos, pero con un gran retraso. Pero estaba funcionando bien donde solo había una pequeña cantidad de datos en la tabla de miembros. Una vez que lo cambiamos para usar una subconsulta, funcionó bien.

Pero mientras tanto, las consultas de combinación funcionan con otras tablas que tienen menos entradas que la tabla principal.

Entonces, creo que las declaraciones de unión y subconsulta funcionan bien y depende de los datos y la situación.

jpk
fuente
3

En estos días, muchos dbs pueden optimizar subconsultas y uniones. Por lo tanto, solo tiene que examinar su consulta usando explicar y ver cuál es más rápido. Si no hay mucha diferencia en el rendimiento, prefiero usar subconsultas, ya que son simples y fáciles de entender.

Canción Eunwoo
fuente
1

Solo estoy pensando en el mismo problema, pero estoy usando subconsulta en la parte FROM. Necesito conectar y consultar desde tablas grandes, la tabla "esclava" tiene 28 millones de registros, ¡pero el resultado es solo 128, tan pequeño como resultado, grandes datos! Estoy usando la función MAX () en él.

Primero estoy usando LEFT JOIN porque creo que esa es la forma correcta, mysql puede optimizar, etc. La segunda vez solo para probar, reescribo para sub-seleccionar contra JOIN.

Tiempo de ejecución de IZQUIERDA UNIR: 1.12s Tiempo de ejecución de SUB-SELECT: 0.06s

¡18 veces más rápido la subselección que la unión! Justo en el chokito adv. La subselección se ve terrible pero el resultado ...

Karoly Szabo
fuente
-1

Si desea acelerar su consulta usando join:

Para "unión / unión interna", no use la condición where en lugar de usarla en la condición "ON". P.ej:

     select id,name from table1 a  
   join table2 b on a.name=b.name
   where id='123'

 Try,

    select id,name from table1 a  
   join table2 b on a.name=b.name and a.id='123'

Para "Unión izquierda / derecha", no use en la condición "ON", porque si usa la unión izquierda / derecha obtendrá todas las filas para cualquier tabla. Por lo tanto, no tiene sentido usarla en "On". Entonces, trate de usar la condición "Dónde"

sam ruben
fuente
Esto depende del servidor SQL y de la complejidad de la consulta. Muchas implementaciones de SQL optimizarían consultas simples como esta para obtener el mejor rendimiento. ¿Quizás proporcione un nombre de servidor y una versión de ejemplo donde este comportamiento mejora la respuesta?
Trendfischer