MySQL 在同一张表上有 2 个 LEFT JOIN
Posted
技术标签:
【中文标题】MySQL 在同一张表上有 2 个 LEFT JOIN【英文标题】:MySQL with 2 LEFT JOINs on the same table 【发布时间】:2009-05-23 16:28:38 【问题描述】:我正在尝试运行此查询:
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
这会给我一些文章(来自nt_noticias
),我的想法是同时从mm_files
表中获取Video
和Audio
文件。
发生的情况是,当我有一篇有声音和视频的文章时,mysql 会返回 4 行:
有声音(视频为空) 有视频(声音为空) 全部为空 有声音和视频如何“强制”它在每篇文章中只返回一行并关联任何现有的视频和音频?我在这里做错了什么?
【问题讨论】:
【参考方案1】:我认为你想要这样的东西:
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
我的想法是这样的:
您最多需要一个音频文件和一个视频文件。每个Noticia
有多个文件可用,因此您需要确保每种类型最多有一个文件进入连接。这也意味着您必须加入ntNoticias_mmFiles
表两次——每种类型一次。
这就是连接条件中的子查询应该做的事情:每种文件类型选择一行。从那里继续,您可以将其余数据加入其中,就像您已经做的那样。
【讨论】:
LIMIT 子句在查询中不起作用,因为我在 MySQL 5.0 上,但我用 GROUP BY noticia_id 替换它并且工作得很好。谢谢一堆! 我总是忘记 MySQL 不处理子查询中的 LIMIT。 :-) 但我很高兴能帮上忙。【参考方案2】:JOIN 将返回所有组合,这就是问题所在。 如果每篇文章只有一个音频和/或视频文件,那么您可能需要查看子选择。 在 SQL Server 中,这看起来像(未经测试的代码):
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
请注意,在大型数据集上,这可能会表现不佳,因为此示例中的子选择是针对返回到外部查询的每一行单独执行的。例如,如果您返回 10,000 篇文章,那么实际上将在服务器上执行总共 20,001 个查询。 还有其他可能的答案来克服这个问题,但他们会更多地参与(我怀疑你可以用派生表做一些事情,但目前我还没有想到)。
【讨论】:
【参考方案3】:您可能希望将该连接查询优化为视图。这是一个很大的查询,而且有这么多的连接,效率会很低。此外,视图可以帮助您调试连接,并且通过允许您分别编写连接(在视图中)和 WHERE 子句(在视图中的选择中)来帮助调试查询,从而基本上简化了操作。
【讨论】:
以上是关于MySQL 在同一张表上有 2 个 LEFT JOIN的主要内容,如果未能解决你的问题,请参考以下文章
MySQL INSERT 在同一张表上使用带有 COUNT() 的子查询