2011-01-30 4 views
5

A menudo me encuentro realizando un par de independientes se une a una mesa. Por ejemplo, supongamos que tenemos la tabla collections, que tiene relaciones independientes de uno a N con photos y songs, donde N es de cero a muchos.En lugar de unir de forma independiente varias tablas, ¿usar consultas por separado?

Ahora, digamos que queremos obtener una colección, y ambos sus fotos y canciones (independientemente) asociadas.

que se utilizan normalmente algo como esto:

SELECT 
    collections.collectionid as collectionid, 
    photos.name as photo_name, 
    songs.name as song_name 

FROM collections 
    LEFT JOIN photos ON collections.collectionid = photos.collectionid 
    LEFT JOIN songs ON collections.collectionid = songs.collectionid 

WHERE collections.collectionid = 14 

Por supuesto, dada a participar en una mesa a otras dos tablas, si la primera combinación da como resultado M filas y el segundo en N filas, da M * N filas . Esto parece poco óptimo en términos de tráfico de base de datos y rendimiento.

+--------------+------------+-----------+ 
| collectionid | photo_name | song_name | 
+--------------+------------+-----------+ 
| 14   | 'x'  | 'a'  | \ 
| 14   | 'x'  | 'b'  | - Each photo is returned 3 times, 
| 14   | 'x'  | 'c'  |/because 3 songs are returned. 
| 14   | 'y'  | 'a'  | \ 
| 14   | 'y'  | 'b'  | 
| 14   | 'y'  | 'c'  |/
+--------------+------------+-----------+ 

Como alternativa, se pueden realizar dos casillas: dos consultas separadas, cada una de unirse collections a una mesa diferente, dando M + N filas:

SELECT 
    collections.collectionid as collectionid 
    song.name as song_name 
FROM collections 
    LEFT JOIN songs on collections.collectionid = songs.collectionid 
WHERE collections.collectionid = 14 

y:

SELECT 
    collections.collectionid as collectionid 
    photos.name as photo_name 
FROM collections 
    LEFT JOIN photos on collections.collectionid = photos.collectionid 
WHERE collections.collectionid = 14 

dando:

+--------------+------------+ +--------------+------------+ 
| collectionid | song_name | | collectionid | photo_name | 
+--------------+------------+ +--------------+------------+ 
| 14   | 'a'  | | 14   | 'x'  | 
| 14   | 'b'  | | 14   | 'y'  | 
| 14   | 'c'  | +--------------+------------+ 
+--------------+------------+ 

Mi pregunta: ¿Cuál es la mejor manera de manejar esto?

Ninguno de los anteriores parece óptimo. Entonces, ¿hay otra manera de que resulte en filas M + N, pero se puede hacer en una sola consulta?

+1

En MySQL puede usar 'group_concat' para devolver 1 fila por grupo. No tengo idea de las implicaciones de rendimiento. –

+0

Gracias Martin. Whoa, funky. Por lo que puedo ver, eso agrega los resultados después de la generación, por lo tanto, no cambia el problema de rendimiento "M *" de la consulta en sí. Sin embargo, como reduce el número de filas, debería reducir la longitud de los bucles, en PHP decir, que llaman 'mysql_fetch_assoc'. –

+0

Así es como las uniones funcionan en SQL, casi necesita 2 consultas si no se desea este resultado. – nos

Respuesta

5

Su primera opción (dos JOINs independientes) no parece proporcionarle un conjunto de resultados muy útil (porque las dos tablas subsidiarias producen un producto semicircular y tiene que duplicar los resultados en el código de la aplicación)

La segunda opción (dos consultas separadas) está bien, a menos que desee tratar los resultados de las dos consultas como un conjunto único para fines de presentación (por ejemplo, ordenarlos por un campo de fecha).

La mejor solución, creo, es la combinación de las dos consultas en uno con UNION ALL, produciendo un único conjunto de resultados con sólo las filas que realmente quiere:

SELECT 
    collections.collectionid as collectionid, 
    photos.name as photo_name, 
    'photo' as document_type 
FROM collections 
    LEFT JOIN photos on collections.collectionid = photos.collectionid 
WHERE collections.collectionid = 14 
UNION ALL 
SELECT 
    collections.collectionid as collectionid, 
    song.name as photo_name 
    'song' as document_type 
FROM collections 
    LEFT JOIN songs on collections.collectionid = songs.collectionid 
WHERE collections.collectionid = 14 

Este tipo de conjunto de resultados puede ser ORDERed BY cualquier campo en todo el conjunto combinado de registros, lo que permite (por ejemplo) obtener los 20 documentos más recientes adjuntos a la colección, independientemente del tipo que sean.

+0

Muchas gracias. Dado que debería llevar apenas más trabajo que las dos consultas separadas, pero las combina, también parece ser mejor que cualquiera de las opciones presentadas en la Q. –

0

Parece que la relación entre fotos y permisos no está definida, lo que da como resultado la combinación cruzada de la que habla. Sí, a su valor nominal, hacer dos consultas es mejor que lo que tienes. Sin embargo, la verdadera pregunta es ¿por qué las fotos y los permisos no tienen una relación basada en la clave?

Pero quizás no entiendo su esquema general. Quizás todos los permisos pertenecen a un solo usuario. En caso afirmativo, consideraría colocar todos los permisos en una sola fila (de varias columnas o en un blob XML), en lugar de hacerlo en varias filas. Si lo hace, permitirá que una única consulta obtenga todos los valores sin que se produzca una combinación cruzada involuntaria.

+0

Los permisos se aplican a las colecciones, no a las fotos. No hay relación entre fotos y permisos. 'permisos' tiene columnas' collectionid', 'userid', y' permission'. El punto es que estamos haciendo dos uniones independientes de la tabla 'collections', usando su clave principal: una para' photos', y una para 'permisos'. La pregunta es si sería mejor separarlos o realizarlos de otra manera. –

+0

@Benji: sin relación entre permisos y fotos, se deben realizar dos consultas por separado. –

+3

¿Puedes ampliar eso en absoluto? Por ejemplo, ¿su razonamiento se basa en el rendimiento, la claridad o la pureza RDBMS? –

Cuestiones relacionadas