时间:2011-01-30 23:24:37

标签: sql mysql join

我经常发现自己在桌子上执行了几个独立的连接。例如,假设我们有表collections,它与photossongs具有独立的一对一关系,其中N从零到多。

现在,我们想要一个集合,两个它(独立)相关的照片和歌曲。

我通常会使用这样的东西:

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

当然,如果第一个连接产生M行而第二个连接N行,则将一个表左连接到另外两个表,给出M * N行。就数据库流量和性能而言,这似乎不是最理想的。

+--------------+------------+-----------+
| 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'       | /
+--------------+------------+-----------+

或者,您可以执行两项选择:两个单独的查询,每个查询将collections加入另一个表,提供M + N行:

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

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

,并提供:

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

我的问题:处理此问题的最佳方法是什么?

上述两种情况都不是最佳选择。那么,是否有另一种方法可以产生M + N行,但是可以在一个查询中完成?

2 个答案:

答案 0 :(得分:5)

您的第一个选项(两个独立的JOIN)似乎没有为您提供非常有用的结果集(因为这两个子表生成半笛卡尔积,您必须在应用程序代码中重复删除结果)

第二个选项(两个单独的查询)是可以的,除非您想将两个查询的结果视为单个集合用于演示目的(例如,通过日期字段将它们一起排序)。

我认为,最好的解决方案是将两个查询合并为一个UNION ALL,生成一个只包含您实际需要的行的结果集:

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

这种结果集可以是ORDERed BY整个组合记录集中的任何字段,允许(例如)获取附加到集合的20个最新文档,无论它们是什么类型。< / p>

答案 1 :(得分:0)

似乎照片和权限之间的关系未定义,这导致您说的交叉连接。是的,从表面上看,做两个查询比你的要好。但是,真正的问题是为什么照片和权限没有基于密钥的关系?

但也许我不理解你的整体架构。也许所有权限都与单个用户有关。如果是,那么我会考虑将所有权限放在一行(多列或多个blob)中,而不是多行。这样做将允许单个查询获取所有值,而不会导致无意的交叉连接。