简体   繁体   English

而不是独立地联接多个表,而是使用单独的查询?

[英]Instead of independently joining multiple tables, use separate queries?

I often find myself performing a couple of independent joins off a table. 我经常发现自己在桌子旁进行了几次独立的联接。 For instance, say we have the table collections , which has independent one-to-N relationships with both photos and songs , where N is from zero to many. 例如,假设我们有一个table collections ,它与photossongs具有独立的一对一关系,其中N是从零到很多。

Now, say we want to get a collection, and both its (independently) associated photos and songs. 现在,假设我们希望得到一个集合,它的两个 (独立)相关的照片和歌曲。

I would typically use something like this: 我通常会使用这样的东西:

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

Of course, left-joining one table to two other tables, if the first join results in M rows and the second in N rows, gives M * N rows. 当然,如果第一个联接导致M行,第二个N行,则将一个表左联接到另外两个表,则得到M * N行。 This would seem suboptimal in terms of database-traffic and performance. 就数据库流量和性能而言,这似乎不是最佳的。

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

Alternatively, you can perform two selects: two separate queries, each joining collections to a different table, giving M + N rows: 或者,您可以执行两个选择:两个单独的查询,每个查询将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

and: 和:

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

giving: 给予:

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

My question: What is the best way to handle this? 我的问题:处理此问题的最佳方法是什么?

Neither of the above seems optimal. 以上都不是最佳选择。 So, is there another way that results in M + N rows, yet can be done in a single query? 那么,还有另一种方法可以导致M + N行,但可以在单个查询中完成吗?

Your first option (two independent JOINs) doesn't seem to provide you with a very useful result set (because the two subsidiary tables produce a semi-cartesian product and you have to de-duplicate the results in your application code). 您的第一个选择(两个独立的JOIN)似乎没有为您提供非常有用的结果集(因为两个子表产生了半笛卡尔乘积,您必须在应用程序代码中删除重复的结果)。

The second option (two separate queries) is okay, unless you want to treat the results of the two queries as a single set for presentation purposes (for instance, sort them all together by a date field). 第二个选项(两个单独的查询)是可以的,除非您出于演示目的将这两个查询的结果视为一个单独的集合(例如,按日期字段将它们全部排序)。

The best solution, I think, is to combine the two queries into one with UNION ALL , producing a single result set with only the rows you actually want: 我认为,最好的解决方案是使用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

This kind of result set can be ORDERed BY any field across the entire, combined set of records, allowing (for instance) to get the 20 most recent documents attached to the collection regardless of what type they are. 这种结果集可以按整个组合记录集的任何字段进行ORDERed BY ,从而(例如)允许获取该集合附带的20个最新文档,而不管它们是什么类型。

It does seem that the relationship between photos and permissions is undefined, which results in the cross-join you speak of. 看来照片和权限之间的关系是不确定的,这导致您所说的交叉联接。 Yes, at face value, doing two queries is better than what you have. 是的,从表面上看,执行两个查询比您拥有的要好。 However, the real question is why do photos and permissions have no key-based relationship? 但是,真正的问题是为什么照片和权限没有基于密钥的关系?

But perhaps I'm not understanding your overall schema. 但也许我不了解您的整体架构。 Perhaps all the permissions pertain to a single user. 也许所有权限都属于一个用户。 If yes, then I'd consider placing all the permissions on a single row (of several columns or in an XML blob), rather than in several rows. 如果是,那么我将考虑将所有权限放在一行(几列或XML Blob)中,而不是几行中。 Doing so would permit a single query to fetch all the values without resulting in an unintentional cross-join. 这样做将允许单个查询获取所有值,而不会导致意外的交叉联接。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM