skip to main |
skip to sidebar
RSS Feeds
Un blog donde hablar de software, comics, mangas, peliculas, series, tecnología, libros, videojuegos y todo lo que se pase por nuestra procastrinadora cabeza
Un blog donde hablar de software, comics, mangas, peliculas, series, tecnología, libros, videojuegos y todo lo que se pase por nuestra procastrinadora cabeza
10:40
Publicado por Mike_HDF
Para entender el post necesitas: Tener conocimientos de SQL, aunque no necesariamente de SQL Server (otra cosa es que te sea útil si no lo utilizas).
Cuando uno está acostumbrado al comportamiento de un motor de base de datos específico, iniciarse con uno distinto puede resultar un problema; ya que es normal encontrar casos particulares en los que el comportamiento de uno y otro difieren, sobre todo a la hora de manejar valores nulos.
En mi caso, ya estaba acostumbrado a trabajar con la base de datos Informix de la compañía IBM, de las más potentes y rápidas que he utilizado sin lugar a dudas, cuando empecé a utilizar / experimentar la versión Express de SQL Server 2005. Tenía que migrar una aplicación de un lenguaje (Dynamic 4GL) a su nueva versión harto mejorada (Genero 4GL) y de una base de datos a otra.
Todo fue bien hasta que tuve que hacer una SQL con un "potito" OUTER JOIN.
En el programa en cuestión, necesitabamos relacionar la tabla de artículos con la tabla de existencias, pero queríamos que salieran todos los artículos que pertenecieran, por ejemplo, a la familia 'AJUSA', sin importar si existía o no el registro correspondiente en la tabla de existencias. Además, para el presente ejemplo, sólo vamos a consultar el año 2008.
La consulta a ejecutar en Informix es muy simple:
SELECT articulo.*,existencia.*
FROM articulo, OUTER existencia
WHERE articulo.fam_art = existencia.fam_art
AND articulo.cod_art = existencia.cod_art
AND articulo.fam_art = 'AJUSA'
AND existencia.ejercicio = 2008
ORDER BY articulo.fam_art
Para comprobar que la sentencia es correcta, basta con hacer un SELECT COUNT de los registros de la tabla de artículos cuya familia sea 'AJUSA' y compararlo con el resultante de esta misma consulta. Ambas sentencias devolverán el mismo número de registros. Y si tenemos pocos artículos podemos asegurarnos comparando, os aseguro que no falla.
Ahora bien, si hacemos la consulta equivalente en SQL Server, que sería:
SELECT articulo.*,existencia.*
FROM existencia RIGHT OUTER JOIN articulo
ON existencia.fam_art = articulo.fam_art
AND existencia.cod_art = articulo.cod_art
WHERE articulo.fam_art = 'AJUSA'
AND existencia.ejercicio = 2008
ORDER BY articulo.fam_art
Tendremos una desagradable sorpresa; ya que sólo devolverá los registros que en ambas tablas existan con los criterios especificados o, lo que es lo mismo, HACE UN PUTO INNER JOIN.
Rebuscando por ahí encontré esta página en el que veía confirmada esta afirmación y la forma de solucionar el problema. La forma de trabajar de SQL Server es un poco especialita, por decirlo suavemente: primero se hace el filtrado de la clausula WHERE y ¡¡DESPUÉS!! el OUTER JOIN.
La solución propuesta en la web de sqlteam consiste en incluir los criterios de selección en la clausula ON del OUTER JOIN. Para que nos entendamos, si queremos sacar TODOS los artículos relacionados con la tabla de existencias que sean del 2008 (existan o no en esta tabla); la consulta necesaria sería:
SELECT articulo.*,existencia.*
FROM existencia RIGHT OUTER JOIN articulo
ON existencia.fam_art = articulo.fam_art
AND existencia.cod_art = articulo.cod_art
AND existencia.ejercicio = 2008
Pero en nuestro caso tenemos un problema adicional; y es que sólo necesitamos los artículos cuya familia es 'AJUSA'. El campo fam_art forma parte del OUTER JOIN, con lo que esta condición sería ignorada por el motor de la base de datos; de modo que la consulta:
SELECT articulo.*,existencia.*
FROM existencia RIGHT OUTER JOIN articulo
ON existencia.fam_art = articulo.fam_art
AND existencia.cod_art = articulo.cod_art
AND articulo.fam_art = 'AJUSA'
AND existencia.ejercicio = 2008
... daría el mismo resultado que la sentencia SQL anterior; vamos, que saca todos los artículos tengan o no tengan existencias en el 2008.
Pero la solución está cerca atendiendo al orden de ejecución de las clausulas, porque lo que debemos hacer es utilizar la clausula ON para indicar los filtros sobre la tabla en la que se especifica el OUTER (en nuestro caso, '"existencia") y después emplear la clausula WHERE para la tabla de la que queremos extraer todos los registros ("articulo" en este ejemplo):
SELECT articulo.*,existencia.*
FROM existencia RIGHT OUTER JOIN articulo
ON existencia.fam_art = articulo.fam_art
AND existencia.cod_art = articulo.cod_art
AND existencia.ejercicio = 2008
WHERE articulo.fam_art = 'AJUSA'
Esta consulta devuelve los registros que deseábamos obtener.
Los OUTER JOIN se utilizan muy rara vez y es difícil encontrar información sobre este dichoso problema; así pues, decidí que tan preciada información tenía que estar en este blog, que tantas visitas recibe, para que el conocimiento se propague rápidamente XD.
lunes, abril 21, 2008 8:37:00 p. m.
Hola, muchas gracias por tu artículo. Soy un novato en esto de los "JOINs", pero me hacen falta para resolver un problema de una aplicación.
Estoy con el "dichoso" SQL Server y lo estaba haciendo con INNER JOIN, LEFT JOIN y RIGHT JOIN.
¿Qué diferencias hay con OUTER JOIN?
Muchas
Gracias
Saludos
JUAN (Granada)
miércoles, abril 23, 2008 1:53:00 p. m.
Bueno, LEFT/RIGHT JOIN y LEFT/RIGHT OUTER JOIN hacen exactamente lo mismo, como bien sabrás.
La diferencia que tiene el INNER JOIN con respecto a cualquier OUTER es que con INNER JOIN sólo obtendrás aquellos registros que existan en las dos tablas relacionadas por el INNER JOIN; y con el LEFT JOIN obtendrás todos los registros de la tabla que esté a la izquierda de la propia palabra OUTER, encadenándose con tuplas (filas) de valores nulos por cada registro no encontrado en la tabla colocada a la derecha del OUTER JOIN. En el RIGHT JOIN pasa al contrario.
Siguiendo con el ejemplo del artículo, hago un RIGHT OUTER JOIN porque los registros que quiero sacar, exista o no exista correspondencia en la tabla "existencia", son los de la tabla "articulo" y, dado que la tabla "articulo" está a la derecha del OUTER, tengo que especificar RIGHT. También conseguiría lo mismo si lo hubiese hecho así:
FROM articulo LEFT OUTER JOIN existencia
Ya que los registros de la tabla "articulo" son los que quiero sacar y debo usar un LEFT puesto que he puesto el nombre de la tabla en la parte izquierda del OUTER.
Espero que haya resuelto tu duda (a tiempo) y muchas gracias por tu comment ;)
lunes, febrero 16, 2009 4:02:00 p. m.
Gracias por la información. Estuve trabajando con SQL Server y siempre se me presentan problemas de este tipo. Ahora si, espero poder resolver algunas de mis dudas (y consultas SQL) con este tema.
Saludos y éxitos
lunes, febrero 16, 2009 10:58:00 p. m.
Gracias a ti por tu comentario, y suerte con ese SQL Server; salvo por el follón de los OUTER, lo cierto es que no funciona nada mal.
Saludos.
miércoles, abril 29, 2009 7:42:00 p. m.
Hasta ahora no había encontrado la solución a dicho problema, muchas gracias por la publicación del artículo.
miércoles, mayo 06, 2009 11:47:00 p. m.
Me sirvio bastante, gracias.
viernes, noviembre 27, 2009 6:01:00 p. m.
Gracias por la publicacion, me ha servido, ahora ya se como utilizar bien los outer
martes, febrero 16, 2010 5:30:00 p. m.
Por que no usar un left join en vez de complicar las cosas
domingo, octubre 10, 2010 5:53:00 p. m.
Muchas gracias funcionó de maravillas, aca les dejo otro ejemplo mas complejo... se trata de unos movimientos de cuentas contables vs el plan de cuentas, estos estan ingresados por empresa, año y mes de registro. Saludos desde Perú.
DECLARE @CodEmpresa char(2),
@Anio char(4),
@Mes char(2),
@Moneda char(1)
SELECT @codempresa='01', @anio='2010', @mes='04', @moneda='N'
SELECT p.CodCuenta, p.Descripcion, d.CodSubdiario, d.NroAsiento
FROM VoucherD d RIGHT OUTER JOIN PlanCuentas p ON (d.CodEmpresa = p.CodEmpresa AND d.Anio = p.Anio AND d.CodCuenta = p.CodCuenta AND d.CodEmpresa = @CodEmpresa AND
d.Anio = @Anio AND d.Mes = @Mes)
WHERE (p.CodEmpresa = @CodEmpresa) AND (p.Anio = @Anio)
ORDER BY p.CodCuenta
lunes, octubre 11, 2010 8:29:00 p. m.
Me alegra saber que este post sigue siendo útil.
Saludos.
jueves, noviembre 18, 2010 4:11:00 p. m.
Muchas gracias por la información, en serio, muy util.
Un saludo
miércoles, diciembre 01, 2010 12:26:00 a. m.
Gracias mano, no me funcionaba el puto left join :P
Pero ya se resolvió gracias a tu post
domingo, julio 24, 2011 11:25:00 p. m.
Gracias!!!, excelente y claramente explicado.
jueves, diciembre 22, 2011 6:14:00 p. m.
Maestro, gracias por compartir esto que lo estoy usando, gracias
jueves, diciembre 29, 2011 6:03:00 p. m.
Gracias maestro sin ti la vida seria mas dificil
jueves, marzo 22, 2012 7:28:00 a. m.
EXCELENTE ARTICULO, TODA UNA NOCHE BUSCANDO COMO RESOLVERLO Y AL LEER TU ARTICULO EN 1 MINUTO SE RESOLVIO, NO PONGO MI EJEMPLO PORQUE ESTA LARGUISIMO PERO MIL GRACIAS!
jueves, marzo 22, 2012 1:59:00 p. m.
Me alegra ver que el artículo sigue siendo útil a tanta gente.
Saludos a todos ^^.
domingo, abril 06, 2014 6:40:00 p. m.
Te comprendo, ya que tambien tengo la experiencia que describes de migrar de Informix a SQL y tienes que usar el famoso OUTER, es bastante complicado. Te agradezco por colaborarnos con la explicacion de como usar el outer join en SQL, estuve buscando en google y es esporadico encontrar un ejemplo acerca de este tema en particular. Saludos desde San Pedro Sula, Cortes, Honduras...