¿SQL dejó unirse frente a varias tablas en la línea DESDE?

256

La mayoría de los dialectos de SQL aceptan las siguientes consultas:

SELECT a.foo, b.foo
FROM a, b
WHERE a.x = b.x

SELECT a.foo, b.foo
FROM a
LEFT JOIN b ON a.x = b.x

Ahora, obviamente, cuando necesita una combinación externa, se requiere la segunda sintaxis. Pero al hacer una unión interna, ¿por qué debería preferir la segunda sintaxis a la primera (o viceversa)?

jmucchiello
fuente
1
Guffa: ¿Cómo encontraste eso? Aunque mi pregunta es más una mejor práctica que "cómo lo hago"
jmucchiello
Dado que es la mejor práctica, haga de este un Wiki.
Binoj Antony
1
No creo que nadie haya comentado sobre el rendimiento de estos dos. ¿Alguien puede confirmar o citar algo razonable con respecto a las diferencias significativas?
ahnbizcad
@ahnbizcad Las dos consultas dadas no hacen lo mismo. El primero devuelve lo mismo que un INNER JOIN ON. La implementación es específica de la versión de DBMS, e incluso entonces tiene pocas garantías. Pero las transformaciones de DBMS que equivalen a casos de coma vs INNER JOIN ON / WHERE vs CROSS JOIN WHERE es trivial. Obtenga información sobre la optimización / implementación de consultas de bases de datos relacionales.
Filipinas
¿Tienes una recomendación de recursos? Los manuales gigantes y densos son la razón por la que trato de aprender de aquí.
ahnbizcad

Respuestas:

319

La sintaxis anterior, con solo enumerar las tablas y usar la WHEREcláusula para especificar los criterios de unión, está en desuso en la mayoría de las bases de datos modernas.

No es solo para mostrar, la sintaxis anterior tiene la posibilidad de ser ambigua cuando se usan tanto INNER como OUTER en la misma consulta.

Dejame darte un ejemplo.

Supongamos que tiene 3 tablas en su sistema:

Company
Department
Employee

Cada tabla contiene numerosas filas, unidas entre sí. Tienes múltiples compañías, y cada compañía puede tener múltiples departamentos, y cada departamento puede tener múltiples empleados.

Ok, ahora quieres hacer lo siguiente:

Enumere todas las empresas e incluya todos sus departamentos y todos sus empleados. Tenga en cuenta que algunas empresas aún no tienen departamentos, pero asegúrese de incluirlos también. Asegúrese de recuperar solo los departamentos que tienen empleados, pero siempre enumere todas las empresas.

Entonces haces esto:

SELECT * -- for simplicity
FROM Company, Department, Employee
WHERE Company.ID *= Department.CompanyID
  AND Department.ID = Employee.DepartmentID

Tenga en cuenta que el último es una unión interna, para cumplir con los criterios de que solo desea departamentos con personas.

Ok, entonces que pasa ahora. Bueno, el problema es que depende del motor de la base de datos, el optimizador de consultas, los índices y las estadísticas de la tabla. Dejame explicar.

Si el optimizador de consultas determina que la forma de hacerlo es primero tomar una empresa, luego encontrar los departamentos y luego hacer una unión interna con los empleados, no obtendrá ninguna empresa que no tenga departamentos.

La razón de esto es que la WHEREcláusula determina qué filas terminan en el resultado final, no partes individuales de las filas.

Y en este caso, debido a la unión izquierda, la columna Department.ID será NULL y, por lo tanto, cuando se trata de la UNIÓN INTERNA a Empleado, no hay forma de cumplir esa restricción para la fila Empleado, por lo que no Aparecer.

Por otro lado, si el optimizador de consultas decide abordar primero la unión departamento-empleado y luego hacer una unión izquierda con las empresas, las verá.

Entonces la sintaxis antigua es ambigua. No hay forma de especificar lo que desea, sin tratar con sugerencias de consulta, y algunas bases de datos no tienen ninguna manera.

Ingrese la nueva sintaxis, con esto puede elegir.

Por ejemplo, si desea todas las empresas, como se indica en la descripción del problema, esto es lo que escribiría:

SELECT *
FROM Company
     LEFT JOIN (
         Department INNER JOIN Employee ON Department.ID = Employee.DepartmentID
     ) ON Company.ID = Department.CompanyID

Aquí especifica que desea que la unión departamento-empleado se realice como una unión, y luego deja unirse a los resultados de eso con las empresas.

Además, supongamos que solo desea departamentos que contengan la letra X en su nombre. Una vez más, con las combinaciones de estilo antiguo, también corre el riesgo de perder la compañía, si no tiene departamentos con una X en su nombre, pero con la nueva sintaxis, puede hacer esto:

SELECT *
FROM Company
     LEFT JOIN (
         Department INNER JOIN Employee ON Department.ID = Employee.DepartmentID
     ) ON Company.ID = Department.CompanyID AND Department.Name LIKE '%X%'

Esta cláusula adicional se usa para la unión, pero no es un filtro para toda la fila. Por lo tanto, la fila puede aparecer con información de la compañía, pero puede tener NULL en todas las columnas de departamento y empleado para esa fila, porque no hay departamento con una X en su nombre para esa compañía. Esto es difícil con la sintaxis anterior.

Es por eso que, entre otros proveedores, Microsoft ha desaprobado la sintaxis de combinación externa anterior, pero no la sintaxis de combinación interna anterior, desde SQL Server 2005 y versiones posteriores. La única forma de comunicarse con una base de datos que se ejecuta en Microsoft SQL Server 2005 o 2008, utilizando la sintaxis de combinación externa de estilo antiguo, es establecer esa base de datos en modo de compatibilidad 8.0 (también conocido como SQL Server 2000).

Además, la forma antigua, al arrojar un montón de tablas en el optimizador de consultas, con un montón de cláusulas WHERE, era similar a decir "aquí estás, haz lo mejor que puedas". Con la nueva sintaxis, el optimizador de consultas tiene menos trabajo que hacer para descubrir qué partes van juntas.

Entonces ahí lo tienes.

IZQUIERDA e INTERIOR es la ola del futuro.

Lasse V. Karlsen
fuente
28
"está en desuso en la mayoría de las bases de datos modernas". --- solo curiosidad, ¿cuáles?
zerkms
10
perdóname, no estoy familiarizado con el operador * =, ¿qué hace? ¡Gracias!
ultrajohn
99
Star = y = Star son (bien estaban) uniones externas derecha e izquierda, ¿o son izquierda y derecha? Ha quedado obsoleto durante años, no los he usado desde SQL Server 6.
Tony Hopkinson
3
La coma no está en desuso. La OUTER JOINsintaxis nunca estándar *=/ =*/ *=*está en desuso.
philipxy
1
Esta respuesta ni siquiera responde a la pregunta, que no se trata de uniones externas. La única afirmación que hace sobre coma vs INNER JOIN ON, re optimización, está mal.
philipxy
17

La sintaxis JOIN mantiene las condiciones cerca de la tabla a la que se aplican. Esto es especialmente útil cuando une una gran cantidad de tablas.

Por cierto, también puedes hacer una unión externa con la primera sintaxis:

WHERE a.x = b.x(+)

O

WHERE a.x *= b.x

O

WHERE a.x = b.x or a.x not in (select x from b)
Andomar
fuente
2
La sintaxis * = está en desuso en MS SQLServer y por una buena razón: no solo hace que sea más difícil de leer, sino que no hace lo que la gente piensa que hace y NO es lo mismo que una UNIÓN IZQUIERDA de aspecto similar. La sintaxis (+) no me es familiar; ¿Qué implementación de SQL hace eso?
Euro Micelli
2
La otra sintaxis es utilizada por Oracle, al menos.
Lasse V. Karlsen
44
Nunca use la sintaxis de SQL Server * =, NO dará resultados consistentes ya que a veces se interpretará como una unión cruzada, no una unión izquierda. Esto es así incluso desde SQL Server 2000. Si tiene algún código que lo use, debe corregirlo.
HLGEM
12

La primera forma es el estándar más antiguo. El segundo método se introdujo en SQL-92, http://en.wikipedia.org/wiki/SQL . El estándar completo se puede ver en http://www.contrib.andrew.cmu.edu/~shadow/sql/sql1992.txt .

Pasaron muchos años antes de que las compañías de bases de datos adoptaran el estándar SQL-92.

Entonces, la razón por la que se prefiere el segundo método es el estándar SQL de acuerdo con el comité de estándares ANSI e ISO.

Dwight T
fuente
,sigue siendo estándar onnecesitaba ser introducido solo por outer joinuna vez que también se introdujeron subselecciones.
philipxy
12

Básicamente, cuando su cláusula FROM enumera tablas como esta:

SELECT * FROM
  tableA, tableB, tableC

el resultado es un producto cruzado de todas las filas en las tablas A, B, C. Luego aplica la restricción WHERE tableA.id = tableB.a_idque arrojará una gran cantidad de filas, luego más ... AND tableB.id = tableC.b_idy luego debería obtener solo aquellas filas que realmente le interesen en.

Los DBMS saben cómo optimizar este SQL para que la diferencia de rendimiento al escribir esto usando JOIN sea insignificante (si existe). El uso de la notación JOIN hace que la declaración SQL sea más legible (en mi humilde opinión, no usar combinaciones convierte la declaración en un desastre). Al usar el producto cruzado, debe proporcionar criterios de combinación en la cláusula WHERE, y ese es el problema con la notación. Estás abarrotando tu cláusula WHERE con cosas como

    tableA.id = tableB.a_id 
AND tableB.id = tableC.b_id 

que solo se usa para restringir el producto cruzado. La cláusula WHERE solo debe contener RESTRICCIONES al conjunto de resultados. Si combina criterios de unión de tabla con restricciones de conjunto de resultados, usted (y otros) encontrarán su consulta más difícil de leer. Definitivamente debe usar JOINs y mantener la cláusula FROM una cláusula FROM y la cláusula WHERE una cláusula WHERE.

Peter Perháč
fuente
10

Se prefiere el segundo porque es mucho menos probable que resulte en una unión cruzada accidental al olvidar poner la cláusula where. Una unión sin cláusula activa fallará la verificación de sintaxis, una unión de estilo antiguo sin cláusula where no fallará, hará una unión cruzada.

Además, cuando luego tiene que unirse a la izquierda, es útil para el mantenimiento que todos estén en la misma estructura. Y la sintaxis anterior ha estado desactualizada desde 1992, ya es hora de dejar de usarla.

Además, he descubierto que muchas personas que usan exclusivamente la primera sintaxis realmente no entienden las uniones y comprender las uniones es fundamental para obtener resultados correctos al realizar consultas.

HLGEM
fuente
6

Creo que hay algunas buenas razones en esta página para adoptar el segundo método, utilizando JOIN explícitos. Sin embargo, el factor decisivo es que cuando los criterios JOIN se eliminan de la cláusula WHERE, es mucho más fácil ver los criterios de selección restantes en la cláusula WHERE.

En declaraciones SELECT realmente complejas, es mucho más fácil para un lector comprender lo que está sucediendo.

Alan G
fuente
5

La SELECT * FROM table1, table2, ...sintaxis está bien para un par de tablas, pero se vuelve exponencial ( no necesariamente una declaración matemáticamente precisa ) cada vez más difícil de leer a medida que aumenta el número de tablas.

La sintaxis de JOIN es más difícil de escribir (al principio), pero hace explícito qué criterios afectan qué tablas. Esto hace que sea mucho más difícil cometer un error.

Además, si todas las combinaciones son internas, ambas versiones son equivalentes. Sin embargo, en el momento en que te unes a OUTER en cualquier parte de la declaración, las cosas se vuelven mucho más complicadas y prácticamente garantiza que lo que escribas no consultará lo que crees que escribiste.

Euro Micelli
fuente
2

Cuando necesita una unión externa, la segunda sintaxis no siempre es necesaria:

Oráculo:

SELECT a.foo, b.foo
  FROM a, b
 WHERE a.x = b.x(+)

MSSQLServer (aunque ha quedado en desuso en la versión 2000) / Sybase:

SELECT a.foo, b.foo
  FROM a, b
 WHERE a.x *= b.x

Pero volviendo a tu pregunta. No sé la respuesta, pero probablemente esté relacionado con el hecho de que una unión es más natural (sintácticamente, al menos) que agregar una expresión a una cláusula where cuando estás haciendo exactamente eso: unión .

Pablo Santa Cruz
fuente
El servidor SQL ha desaprobado esa sintaxis de unión izquierda e incluso en SQL Server 2000 no siempre dará resultados correctos (a veces hace una unión cruzada en lugar de una unión izquierda) y nunca debe usarse en SQL Server.
HLGEM
@HLGEM: Gracias por la información. Voy a ACTUALIZAR mi publicación para reflejar lo que estás diciendo.
Pablo Santa Cruz
0

Escuché que mucha gente se queja de que el primero es demasiado difícil de entender y que no está claro. No veo ningún problema con eso, pero después de tener esa discusión, uso el segundo incluso en INNER JOINS para mayor claridad.

kemiller2002
fuente
1
Fui educado con el hábito de no usar la sintaxis JOIN y hacerlo de la primera manera. Debo admitir que todavía estoy atascado en el hábito muchas veces sólo porque creo que mi cerebro ha sido condicionado a seguir esa lógica, wheras la sintaxis de combinación a veces me parece difícil pensar en.
TheTXI
3
También me enseñaron de esa manera. Cambié mi estilo de codificación, porque la gente lo miraría y no reconocería fácilmente lo que estaba sucediendo. Dado que no existe una diferencia lógica y no puedo encontrar ninguna razón para elegir el primero sobre el segundo, sentí que debería adaptarme a hacer el código más claro para ayudar a otros a comprender lo que escribo.
kemiller2002
0

Para la base de datos, terminan siendo los mismos. Para usted, sin embargo, tendrá que usar esa segunda sintaxis en algunas situaciones. En aras de la edición de consultas que terminan teniendo que usarlo (descubriendo que necesitabas una combinación izquierda donde tenías una combinación directa), y por coherencia, solo trazaría el patrón en el segundo método. Facilitará la lectura de consultas.

Jeff Ferland
fuente
0

Bueno, la primera y la segunda consulta pueden arrojar resultados diferentes porque una IZQUIERDA IZQUIERDA incluye todos los registros de la primera tabla, incluso si no hay registros correspondientes en la tabla de la derecha.

Gavin H
fuente