2012-03-28 27 views
5

Estoy tratando de aprender el estándar ansi-92 SQL, pero parece que no lo entiendo completamente (soy nuevo en el estándar ansi-89 y en las bases de datos en general).¿Entiendo mal las uniones?

En mi ejemplo, tengo tres tablas reino - familia < - < especies (clasificaciones de biología).

  • Puede haber reinos sin especies ni familias.
  • Puede haber familias sin especie ni cariño.
  • Puede haber especies sin reino o familias.

¿Por qué esto puede pasar?

dicen que un biólogo, descubre una nueva especie, pero que no ha clasificado esta en un reino o familia, crea una nueva familia que no tiene especie y no está seguro de lo que debería pertenecer reino, etc.

aquí es un violín (ver la última consulta): http://sqlfiddle.com/#!4/015d1/3

Quiero hacer una consulta que me devuelva todos los reinos, todas las especies, pero no las familias que no tienen ninguna especie, así que hago esto.

select * 
    from reino r 
     left join (
      familia f    
      right join especie e 
       on f.fnombre = e.efamilia 
       and f.freino = e.ereino 
     ) on r.rnombre = f.freino 
      and r.rnombre = e.ereino; 

Lo que creo que esto podría hacer es:

  1. se unen a la familia y la especie como un derecho unirse, lo que trae cada especie, pero no aquellas familias que no tienen especies. Por lo tanto, si una especie no se ha clasificado en una familia, aparecerá con nulo en la familia.

  2. Luego, únete al reino con el resultado como un join de la izquierda, por lo que trae a cada reino, incluso si no hay familias o especies clasificadas en ese reino.

Am I wrong? ¿No debería esto mostrarme las especies que no han sido clasificadas? Si hago la consulta interna, trae lo que quiero. ¿Hay algún problema cuando estoy agrupando cosas?

+0

¿Esto se mete con la optimización de consulta que Oracle no ?, en mi consulta, son innecesarias paréntesis? ¿obtengo el mismo resultado? ¿Cuál es el orden que oráculo hace las instrucciones? – Roger

+0

@outis Debo discrepar respetuosamente de que la sintaxis de unión no anida. [Creo que este artículo aborda este tema] (http://tonyhasler.wordpress.com/2009/02/22/parentheses-in-ansi-join-syntax/) y concluye que los paréntesis se pueden usar para aclarar el orden de operaciones al unir múltiples tablas. –

+0

@MichaelFredrickson: Estoy corregido. Ahí está, SQL/Foundation § 7.6. – outis

Respuesta

2

Usted está justo en su descripción de # 1 ... el problema con su consulta está en el paso # 2.

Cuando haces un left join de reino en (familia & especies), que solicita todos los reinos, incluso si no hay coincidencia (familia & especies) ... sin embargo, esto no devolver cualquiera (familia & especie) combinación que no tiene un reino coincidente.

Una consulta más cerca sería:

select * 
    from reino r 
     full join (
      familia f    
      right join especie e 
       on f.fnombre = e.efamilia 
       and f.freino = e.ereino 
     ) on r.rnombre = f.freino 
      and r.rnombre = e.ereino; 

en cuenta que la left join fue reemplazado con un full join ...

Sin embargo, esto solo devuelve familias que están asociadas con una especie ... no devuelve ninguna familia que esté asociada con reinos pero no con especies.

Después de volver a leer su pregunta, esto es en realidad quieren que quería ...


EDIT: El pensamiento adicional, que podría volver a escribir la consulta de este modo:

select * 
from 
    especie e 
    left join familia f 
     on f.fnombre = e.efamilia 
     and f.freino = e.ereino 
    full join reino r 
     on r.rnombre = f.freino 
     and r.rnombre = e.ereino; 

Creo que esto sería preferible, ya que eliminas el RIGHT JOIN, que generalmente son mal vistas por ser de estilo pobre ... y el paréntesis, que puede ser complicado para las personas analizar correctamente para determinar cuál será el resultado.

+0

Muchas gracias, ahora las cosas están más claras. Gracias por tomarte el tiempo :) – Roger

+0

Solo una pregunta más; lea el comentario de mi pregunta hecha desde outis. ¿Los paréntesis son innecesarios? ¿Esto complica el optimizador de Oracle? – Roger

+0

No estoy de acuerdo con @outis ... pero trabajo principalmente en SQL Server, por lo que esto puede variar en Oracle. Sin embargo, en mi experiencia, el paréntesis puede afectar tanto los resultados como el plan de ejecución. [Aquí hay una fuente que creo que confirma esto en Oracle.] (Http://tonyhasler.wordpress.com/2009/02/22/parentheses-in-ansi-join-syntax/) ...No estoy seguro de cómo reestructuraría esa consulta sin usar el paréntesis (probablemente tendría que cambiarla a una selección secundaria), pero si puede, puede comparar los planes de ejecución entre los dos enfoques para asegurarse de que su enfoque original es aceptable. –

0

intenta utilizar esto:

select * 
from reino r 
join especie e on (r.rnombre = e.ereino) 
join familia f on (f.freino = e.ereino and f.fnombre = e.efamilia) 

no podía ser, que intercambian efamilia y enombre en especie mesa?

+0

No estoy buscando uniones internas, gracias de todos modos. – Roger

2

En caso de que esto ayuda a:

Relacionalmente hablando, [OUTER JOIN es] una especie de matrimonio forzado: se fuerzas tablas en una especie de unión, sí, me refiero a la unión, no unirse, incluso cuando las tablas en cuestión no cumplen con los requisitos usuales para la unión. Hace esto, de hecho, rellenando una o ambas tablas con nulos antes de hacer la unión, por lo tanto, haciendo que cumplan con esos requisitos usuales después de todo. Pero no hay razón por la que el relleno no se debe hacer con los valores apropiados en lugar de los nulos, como en este ejemplo:

SELECT SNO , PNO 
FROM SP 
UNION 
SELECT SNO , 'nil' AS PNO 
FROM S 
WHERE SNO NOT IN (SELECT SNO FROM SP) 

Lo anterior es equivalente a:

SELECT SNO , COALESCE (PNO , 'nil') AS PNO 
FROM S NATURAL LEFT OUTER JOIN SP 

Fuente: SQL and Relational Theory: How to Write Accurate SQL Code By C. J. Date

1

Si quiere que la consulta se reescriba con el menor cambio de lo que tiene, puede cambiar el LEFT unirse a un FULL unirse. Se puede quitar el paréntesis más redundante y la r.rnombre = f.freino de la condición ON:

select * 
from reino r 
    full join      --- instead of LEFT JOIN 
     familia f    
     right join especie e 
      on f.fnombre = e.efamilia 
      and f.freino = e.ereino 
     on r.rnombre = e.ereino; 
           ---removed the: r.rnombre = f.freino  
+0

Quiero el mejor resultado, no me importa si tengo que volver a escribirlo; ¿Qué es lo mejor para la base de datos y por qué? – Roger

+0

Dado que solo desea familias con especies, no se necesita el 'r.rnombre = f.freino'. Eliminar el paréntesis de la estructura de unión puede o no tener ningún efecto, dependiendo del DBMS (y no soy un experto en Oracle para tener una opinión sobre ese punto). –

+0

Solo una menor no. Esta consulta (y la aceptada, por Michael) no incluirá las familias que tienen reinos (pero no tienen especies). –

Cuestiones relacionadas