【问题标题】:Instead of independently joining multiple tables, use separate queries?不是独立连接多个表,而是使用单独的查询?
【发布时间】:2011-01-30 23:24:37
【问题描述】:

我经常发现自己在一张桌子上执行几个独立连接。例如,假设我们有表collections,它与photossongs 具有独立的一对N 关系,其中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 行,但可以在单个查询中完成?

【问题讨论】:

  • 在 MySQL 中,您可以使用 group_concat 每组返回 1 行。不知道性能影响。
  • 谢谢马丁。哇,时髦。据我所知,它在生成后聚合了结果——因此不会改变查询本身的“M*”性能问题。然而,由于它减少了行数,它应该减少循环的长度,在 PHP 中,调用 mysql_fetch_assoc
  • 这就是连接在 SQL 中的工作方式,如果不需要此结果,您几乎需要 2 个查询。
  • 你喜欢什么样的输出?您是否正在为每个匹配对象寻找一排,即两排照片和三排歌曲?

标签: sql mysql join


【解决方案1】:

您的第一个选项(两个独立的 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 个最新文档,无论它们是什么类型。

【讨论】:

  • 非常感谢。由于与两个单独的查询相比,它几乎不需要更多的工作,而是将它们组合起来,它似乎也可能比 Q 中提供的任何一个选项都具有更好的性能。
【解决方案2】:

看起来照片和权限之间的关系是不确定的,这导致了你所说的交叉加入。是的,从表面上看,做两个查询比你所拥有的要好。然而,真正的问题是为什么照片和权限没有基于密钥的关系?

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

【讨论】:

  • 权限适用于收藏,而不是照片。照片和权限之间没有关系。 permissions 具有列 collectioniduseridpermission。关键是我们在collections 表上做两个独立的连接,使用它的主键:一个到photos,一个到permissions。问题是最好将它们分开,还是以不同的方式执行它们。
  • @Benji:权限和照片之间没有关系,那么应该进行两个单独的查询。
  • 你能扩展一下吗?例如,您的推理是基于性能、清晰度还是 RDBMS 纯度?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-11-09
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多