相同的表,多个左连接,具有mysql中的条件

时间:2013-07-25 03:34:07

标签: mysql left-join

简化问题,我有两个表 - item和item_info。

项目如下:

+-----+----------+
| id  | title    |
+-----+----------+
|   1 |   lorem1 |
|   2 |   lorem2 |
|   3 |   lorem3 |
|   4 |   lorem4 |
|   5 |   lorem5 |
+-----+----------+

item_info如下所示:

+-----+----------+---------------+---------------+
| id  | item_id  | field_name    | field_value   |
+-----+----------+---------------+---------------+
|   1 |        1 | tag           | a_tag1        |
|   2 |        1 | tag           | a_tag2        |
|   3 |        1 | series_title  | bob_show      |
|   4 |        1 | tag           | b_tag1        |
|   5 |        1 | tag           | b_tag2        |
|   6 |        2 | tag           | a_tag3        |
|   7 |        2 | tag           | a_tag4        |
|   8 |        2 | series_title  | jane_show     |
|   9 |        2 | tag           | b_tag3        |
|  10 |        2 | tag           | b_tag4        |
+-----+----------+---------------+---------------+

我正试图得到一个看起来像这样的结果:

+-----+----------+---------------+---------------+---------------+
| id  | title    | series_title  | a_tags        | b_tags        |
+-----+----------+---------------+---------------+---------------+
|   1 |   lorem1 | bob_show      | a_tag1,a_tag2 | b_tag1,b_tag2 |
|   2 |   lorem2 | jane_show     | a_tag3,a_tag4 | b_tag3,b_tag4 |
+-----+----------+---------------+---------------+---------------+

我需要将结果集限制为item.id行的细节列表。我试图用多个左连接来做这个:

select i.id as 'id', i.title as 'title', info.field_value as 'series_title',
    GROUP_CONCAT(DISTINCT info2.field_value) as 'a_tags', 
    GROUP_CONCAT(DISTINCT info3.field_value) as 'b_tags'
from item i
left join item_info info on i.id = info.item_id
left join item_info info2 on i.id = info2.item_id
left join item_info info3 on i.id = info3.item_id
and i.id in (1,2)
where info.field_name = 'series_title'
or info2.field_name = 'tag'
and info2.field_value like "a_%"
or info3.field_name = 'tag'
and info3.field_value like "b_%"
group by 1;

我无法判断这是否有效,因为查询是永远存在的,当我们运行永远需要的查询时,我的操作人员会变得胡思乱想。任何人都可以帮我简化查询并确保我以正确的方式构建它吗?

3 个答案:

答案 0 :(得分:0)

我认为这可以做你想要的。

select 
  i.id as 'id', 
  i.title as 'title', 
  series.field_value as 'series_title',
  GROUP_CONCAT(DISTINCT a.field_value) as 'a_tags', 
  GROUP_CONCAT(DISTINCT b.field_value) as 'b_tags'
from item i
  inner join item_info series on i.id = series.item_id
  inner join item_info a on i.id = a.item_id
  inner join item_info b on i.id = b.item_id
where
  i.id in (1, 2)
  and series.field_name = 'series_title'
  and a.field_value like "a\_%"
  and a.field_name = 'tag'
  and b.field_value like "b\_%"
  and b.field_name = 'tag'
group by 1

Here's the SQLFiddle

希望这有帮助。

答案 1 :(得分:0)

看起来这些谓词中的一些属于ON子句,而不是WHERE子句,如果你不想否定LEFT连接,并且i.id上的谓词属于WHERE子句(如果你想要的话)限制返回的行,而不是限制从info3加入的行。

这应该返回指定的结果集:

SELECT i.id AS `id`
     , i.title AS `title`
     , info.field_value AS `series_title`
     , GROUP_CONCAT(DISTINCT info2.field_value ORDER BY info2.field_value) AS `a_tags`
     , GROUP_CONCAT(DISTINCT info3.field_value ORDER BY info3.field_value) AS `b_tags`
  FROM item i
  LEFT
  JOIN item_info info 
    ON info.item_id = i.id
   AND info.field_name = 'series_title'
  LEFT
  JOIN item_info info2
    ON info2.item_id = i.id
   AND info2.field_name = 'tag'
   AND info2.field_value LIKE "a_%"
  LEFT
  JOIN item_info info3 
    ON info3.item_id = i.id
   AND info3.field_name = 'tag'
   AND info3.field_value like "b_%"
 WHERE i.id in (1,2)
 GROUP 
    BY i.id

有可能创建一个大型结果集,其中来自info2(有效)的行与来自info3的行交叉连接。 (来自info2的20个匹配行和来自info3的20个匹配行,将导致项目中每行的20 * 20 = 400行。)

如果只返回一行或两行,则有时相关子查询的性能会更好。 (对于大型集合,情况并非如此,因为子查询的执行次数...每行一次。)

SELECT i.id AS `id`
     , i.title AS `title`
     , ( SELECT info.field_value
           FROM item_info info
          WHERE info.item_id = i.id
            AND info.field_name = 'series_title'
          ORDER BY info.field_value
          LIMIT 1
       ) AS `series_title`
     , ( SELECT GROUP_CONCAT(DISTINCT info2.field_value ORDER BY info2.field_value)
           FROM item_info2 info2
          WHERE info2.item_id = i.id
            AND info2.field_name = 'tag'
            AND info2.field_value LIKE "a_%"
          GROUP BY info2.item_id
       )  AS `a_tags`
     , ( SELECT GROUP_CONCAT(DISTINCT info3.field_value ORDER BY info3.field_value)
           FROM item_info3 info2
          WHERE info3.item_id = i.id
            AND info3.field_name = 'tag'
            AND info3.field_value like "b_%"
          GROUP BY info3.item_id
       )  AS `b_tags`
  FROM item i
 WHERE i.id in (1,2)
 ORDER BY i.id

这也消除了对GROUP BY的需要(如果i.id是唯一的),尽管您可能想要添加ORDER BY,因此结果集以相同的顺序返回(就像它与GROUP BY一样) )

但同样,第二种方法可以是从item返回的大量行上的真正调光器。

答案 2 :(得分:0)

即使通过复杂查询满足您的需求,我认为您必须redesign您的架构。

有点像这样。,

ITEM
---------------------------
 Item_Id (P) |   Title     | series_title (Nullable)
 1              lorem1       bob_show
 2              lorem2       jane_show
 3              lorem3       NULL
 4              lorem4       NULL 
 5              lorem5       NULL  

TAG
------------------
 Tag_id (P) |  Tag_name | item_id (Ref)  | TAG_GroupID (Ref)
   1            a_tag1        1               1
   2            a_tag2        1               1
   3            a_tag3        2               1
   4            a_tag4        2               1
   5            b_tag1        1               2
   6            b_tag2        1               2
   7            b_tag3        2               2
   8            b_tag4        2               2


Tag_Group
-----------------
Tag_GroupID(Primary)| Tag_GroupName
   1                       a_tag
   2                       b_tag

此外,您的查询将成为

select ITEM.ID, ITEM.TITLE, ITEM.SERIES_TITLE ,
(
  SELECT GROUP_CONCAT(Tag_Name) from Tag where ITEM.ID = Tag.Item_ID AND  Tag.Tag_Group_Id = 1
) as 'A_TAGS'
,
(
  SELECT GROUP_CONCAT(Tag_Name) from Tag where ITEM.ID = Tag.Item_ID AND  Tag.Tag_Group_Id = 2
) as 'B_TAGS'

from ITEM

这里的实用跑步方法 - > http://sqlfiddle.com/#!2/45eba/12/0

现在这个只是静态的,您可以使用Dynamic Sql以更动态的方式执行此操作...我会尽快在相同的答案中发布....

别忘了给我你的评论,或者你对这个新架构的看法。

谢谢。