MySQL con 2 LEFT JOIN en la misma mesa
Pregunta
Estoy intentando ejecutar esta consulta:
SELECT
Destaque.destaque, Noticia.id, Noticia.antetitulo,
Noticia.titulo, Noticia.lead, Noticia.legenda,
Noticia.publicacao, Seccao.descricao, Album.pasta,
Foto.ficheiro, Foto.descricao, Cronista.nome,
Cronista.profissao, Cronista.ficheiro,
AudioFile.*, AudioCollection.*, VideoFile.*, VideoCollection.*
FROM
nt_highlights AS Destaque
LEFT JOIN nt_noticias AS Noticia ON Destaque.noticia_id = Noticia.id
LEFT JOIN mm_fotos AS Foto ON Noticia.foto_id = Foto.id
LEFT JOIN nt_temas AS Seccao ON Noticia.tema_id = Seccao.id
LEFT JOIN mm_albuns AS Album ON Foto.album_id = Album.id
LEFT JOIN nt_cronistas AS Cronista ON Cronista.id = Noticia.cronista_id
LEFT JOIN ntNoticias_mmFiles AS Rel ON Rel.noticia_id = Noticia.id
LEFT JOIN mm_files AS AudioFile ON AudioFile.id = Rel.file_id
LEFT JOIN mm_coleccoes AS AudioCollection ON AudioFile.coleccao_id = AudioCollection.id
LEFT JOIN mm_files AS VideoFile ON VideoFile.id = Rel.file_id
LEFT JOIN mm_coleccoes AS VideoCollection ON VideoFile.coleccao_id = VideoCollection.id
WHERE
Destaque.area_id = 1
AND Noticia.paraPublicacao = 1
AND Noticia.publicacao <= NOW()
AND (AudioFile.mimeType != '' OR AudioFile.id IS NULL)
AND (VideoFile.mimeType = '' OR VideoFile.id IS NULL)
ORDER BY
Destaque.destaque
Esto me va a obtener un número de artículos (de nt_noticias
) y la idea es conseguir al mismo tiempo una Video
y un archivo Audio
de la mesa mm_files
.
Lo que sucede es que cuando tengo un artículo con un sonido y un vídeo, MySQL retornará 4 filas:
- con el sonido (vídeo es nulo)
- con el vídeo (sonido es nulo)
- con todos los nulos
- con el sonido y el vídeo
¿Cómo puedo "forzar" a devolver sólo una fila por cada artículo con cualquier video existente y audio asociado? ¿Qué estoy haciendo mal aquí?
Solución
piensan ¿Quieres algo como esto:
SELECT
Destaque.destaque, Noticia.id, Noticia.antetitulo,
Noticia.titulo, Noticia.lead, Noticia.legenda,
Noticia.publicacao, Seccao.descricao, Album.pasta,
Foto.ficheiro, Foto.descricao, Cronista.nome,
Cronista.profissao, Cronista.ficheiro,
AudioFile.*, AudioCollection.*, VideoFile.*, VideoCollection.*
FROM
nt_highlights AS Destaque
LEFT JOIN nt_noticias AS Noticia ON Destaque.noticia_id = Noticia.id
LEFT JOIN mm_fotos AS Foto ON Noticia.foto_id = Foto.id
LEFT JOIN nt_temas AS Seccao ON Noticia.tema_id = Seccao.id
LEFT JOIN mm_albuns AS Album ON Foto.album_id = Album.id
LEFT JOIN nt_cronistas AS Cronista ON Cronista.id = Noticia.cronista_id
LEFT JOIN ntNoticias_mmFiles AS AudioRel ON Rel.noticia_id = Noticia.id
AND AudioRel.file_id IN (
SELECT file_id
FROM ntNoticias_mmFiles
WHERE noticia_id = Noticia.id AND IsAudioFile = 1 /* whatever the check is */
LIMIT 1
)
LEFT JOIN mm_files AS AudioFile ON AudioFile.id = Rel.file_id
LEFT JOIN mm_coleccoes AS AudioCollection ON AudioFile.coleccao_id = AudioCollection.id
LEFT JOIN ntNoticias_mmFiles AS VideoRel ON VideoRel.noticia_id = Noticia.id
AND VideoRel.file_id IN (
SELECT file_id
FROM ntNoticias_mmFiles
WHERE noticia_id = Noticia.id AND IsVideoFile = 1 /* whatever the check is */
LIMIT 1
)
LEFT JOIN mm_files AS VideoFile ON VideoFile.id = Rel.file_id
AND VideoFile.IsVideoFile = 1
LEFT JOIN mm_coleccoes AS VideoCollection ON VideoFile.coleccao_id = VideoCollection.id
WHERE
Destaque.area_id = 1
AND Noticia.paraPublicacao = 1
AND Noticia.publicacao <= NOW()
ORDER BY
Destaque.destaque
Mi idea era la siguiente:
¿Quieres un archivo de audio y un archivo de vídeo, como máximo. Hay varios archivos disponibles por Noticia
, por lo que necesita para asegurarse de que un máximo de un archivo por cada tipo se mete en la unión. Esto también significa que tiene que unirse a la mesa de ntNoticias_mmFiles
dos veces -. Una vez al tipo
Esto es lo que se supone que los sub-consultas en las condiciones de combinación que hacer: Seleccionar una fila por cada tipo de archivo. Y pasando de allí te dejó unirse al resto de los datos en, al igual que ya lo hacen.
Otros consejos
La unión devolverá todas las combinaciones, ese es el problema.
Si sólo tiene un audio y / o archivo de vídeo por artículo entonces es posible que desee ver en subselects.
En SQL Server esto sería algo como (código no probado):
SELECT title,
(select TOP 1 audio from audio where audio.aid = articles.id) as Audio,
(select TOP 1 video from video where video.aid = articles.id) as Video
FROM articles
Tenga cuidado de que en grandes conjuntos de datos esto puede realizarse mal como los subselects en este ejemplo se ejecutan de forma individual para cada fila devuelta a la consulta externa. Por ejemplo, si usted vuelve 10.000 artículos a continuación, un total de 20.001 consultas en realidad se ejecuta en el servidor. Hay otras respuestas posibles para superar esto, pero que se involucren más (sospecho que podría hacer algo con una tabla derivada, pero se me escapa por el momento).
Es posible que desee optimizar esa consulta de combinación en una vista. Es una consulta de gran tamaño, y con el que muchos se une, que va a ser bastante ineficiente. Además, una vista ayuda a depurar las uniones y, básicamente, simplifica al permitir que usted escriba su une (en la vista) y la cláusula WHERE (en su selecto de la vista) por separado, que puede ayudar con la depuración de las consultas.