2010-10-26 8 views
6

Tengo varias tablas con diferentes números y tipos de columnas, y una sola columna en común.¿Cómo combinar resultados de múltiples tablas con diferentes columnas?

+--------+---------+------------+-------------+ 
| person | beardID | beardStyle | beardLength | 
+--------+---------+------------+-------------+ 

+--------+-------------+----------------+ 
| person | moustacheID | moustacheStyle | 
+--------+-------------+----------------+ 

Quiero obtener todos los resultados que coinciden con un valor dado de la columna compartida. Puedo hacerlo mediante múltiples sentencias de selección como esta:

SELECT * FROM beards WHERE person = "bob" 

y

SELECT * FROM moustaches WHERE person = "bob" 

Pero esto requiere múltiples llamadas a la API MySQL, lo que parece ineficiente. Esperaba poder usar UNION ALL para obtener todos los resultados en una sola llamada API, pero UNION requiere que las tablas tengan el mismo número y tipo similar de columnas. Podría escribir una instrucción SELECT que rellenaría manualmente los resultados de cada tabla al agregar columnas con valores NULL, pero eso se volvería rápidamente inmanejable para unas pocas tablas más con algunas columnas más.

Estoy buscando a un conjunto de resultados más o menos así:

+--------+---------+------------+-------------+-------------+----------------+ 
| person | beardID | beardStyle | beardLength | moustacheID | moustacheStyle | 
+--------+---------+------------+-------------+-------------+----------------+ 
| bob | 1  | rasputin | 1   |    |    | 
+--------+---------+------------+-------------+-------------+----------------+ 
| bob | 2  | samson  | 12   |    |    | 
+--------+---------+------------+-------------+-------------+----------------+ 
| bob |   |   |    | 1   | fu manchu  | 
+--------+---------+------------+-------------+-------------+----------------+ 

¿Hay una manera de lograr esto que es rápido y fácil de mantener? ¿O será mejor que ejecute una consulta separada para cada tabla?

Aclaración:

no estoy en busca de un producto cartesiano. No quiero una fila para cada combinación de barba y bigote, quiero una fila para cada barba y una fila para cada bigote.

Así que si hay 3 barbas a juego y 2 bigotes coincidentes que deben recibir 5 filas, no 6.

+1

Si bien se podría hacer en la mayoría de las bases de datos con un 'FULL JOIN', y en MySQL con la solución de ITroubs, dudo seriamente de que se obtenga una ganancia de rendimiento significativa, y probablemente solo estés creando un lío de código oscuro por el que su sucesor no le agradecerá. El código claro y preciso supera una ganancia de rendimiento en los porcentajes muy bajos en la mayoría de las circunstancias. – Wrikken

Respuesta

7

esto debería estar funcionando bien:

SELECT * FROM `beards` b LEFT OUTER JOIN `mustaches` ON (0) WHERE person = "bob" 
UNION ALL 
SELECT * FROM `beards` b RIGHT OUTER JOIN `mustaches` ON (0) WHERE person = "bob" 

usted no tiene que manejar las columnas por sí mismo. la combinación externa izquierda y derecha hacen este trabajo. desafortunadamente mysql no tiene una unión completa. por eso hay que hacerlo de esta manera con una unión

SELECT * FROM `customer` b LEFT OUTER JOIN `charges` ON (0) LEFT OUTER JOIN `day` ON (0) 
UNION 
SELECT * FROM `customer` b RIGHT OUTER JOIN `charges` ON (0) LEFT OUTER JOIN `day` ON (0) 
UNION 
SELECT * FROM `customer` b LEFT OUTER JOIN `charges` ON (0) RIGHT OUTER JOIN `day` ON (0) 

esta es una prueba local que hice

+1

Brillante, funciona como un encanto. Por lo tanto, si entiendo correctamente, está utilizando UNIONES EXTREMAS IZQUIERDA y DERECHA sin especificar los criterios de unión para agregar columnas llenas de valores NULL a los resultados de cada tabla (para que los conjuntos de resultados tengan tablas que coincidan) y UNIÓN de los resultados. – Robert

+2

sí. las uniones son solo para hacer que su resultado tenga todas las columnas para barbas y bigotes y al hacer que la declaración ON sea falsa, solo devuelve todas las líneas de barbas o bigotes dependiendo de la combinación usada – ITroubs

+0

Entonces, para agregar una tercera tabla, agregaría un extra OUTER JOIN ON (0) a cada uno de esos SELECT para la nueva tabla, más UNION ALL y una nueva instrucción selecta OUTER JOINing la nueva tabla a esas dos tablas de la misma manera. – Robert

0

Ingreso en persona ....

es decir,

Seleccionar t1. (Asterisco), t2. (Asterisco) DE barbas t1 INNER JOIN bigotes t2 En t2.person = t1.person

+0

¿Has probado esto? INNER JOIN recupera el producto cartesiano de los registros coincidentes de las dos tablas. Entonces, un resultado para cada combinación de barba y bigote. No es lo que se describe en la pregunta en absoluto. – Robert

+0

@Robert: No, INNER JOIN no produce un producto cartesiano. –

+0

Lo siento, no entiendo ÚNICAMENTE muy bien. ¿Cuál es la diferencia entre INNER JOIN y tomar el producto cartesiano y filtrar en los criterios de unión? – Robert

0
SELECT * 
FROM beards 
     JOIN moustaches 
     ON moustaches.person = beards.person 
WHERE person = "bob" 
+0

Estoy buscando recuperar una fila para cada barba y una fila para cada bigote, no una fila para cada barba y bigote. – Robert

0

Me divertí mucho con esto, no se seguro que es completamente manejable con lo que más tiene que agregar, pero logró el objetivo.

create table beard (
person varchar(20) 
,beardID int 
,beardStyle varchar(20) 
,beardLength int) 

create table moustache(
person varchar(20) 
,moustacheID int 
,moustacheStyle varchar(20)) 


insert into beard 
select 'bob', 1, 'rasputin', 1 
union select 'bob', 2, 'samson', 12 

insert into moustache 
select 'bob', 1, 'fu manchu' 

declare @facialhair table (
person varchar(20) 
,beardID int 
,beardStyle varchar(20) 
,beardLength int 
,moustacheID int 
,moustacheStyle varchar(20)) 

declare @i int 
declare @name varchar(20) 

set @name = 'bob' 
set @i = (select COUNT(*) from beard where person = @name) 
     + (select COUNT(*) from moustache where person = @name) 

print @i 

while @i > 0 
    begin 
     insert into @facialhair (person, beardID, beardStyle, beardLength) 
     select person, beardID, beardStyle, beardLength 
     from beard 
     where person = @name 
    set @i = @[email protected]@ROWCOUNT 

     insert into @facialhair (person, moustacheID, moustacheStyle) 
     select person, moustacheID, moustacheStyle 
     from moustache 
     where person = @name 
    set @i = @[email protected]@ROWCOUNT 
    end 

select * 
from @facialhair 
0

Creo que sería mejor al hacer consultas de datos de cada tabla.

Una de las otras posibilidades es concatenar datos de todas las columnas en una cadena grande (puede elegir algún signo para separar los valores de la columna), entonces debería poder usar la cláusula union all para combinar los resultados de cada consulta, pero luego tendrá que analizar cada fila ... Y los tipos de datos se perderán.

+0

y el mayor inconveniente sería la sobrecarga de cálculo y un tamaño de cadena limitado mysql tiene para group_concat! echa un vistazo a mi solución. Creo que esa sería la forma más fácil. – ITroubs

+0

@ITroubs: buena solución, debería decir – rsc

Cuestiones relacionadas