0

简化问题,我有两个表 - 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;

我不知道这是否有效,因为查询会永远持续下去,而当我们运行永远持续下去的查询时,我的操作人员会变得暴躁。谁能帮我简化查询并确保我以正确的方式构建它?

4

3 回答 3

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

这是 SQLFiddle

希望这可以帮助。

于 2013-07-25T03:56:04.487 回答
0

如果您不想否定 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 返回的大量行来说可能是一个真正的调光器。

于 2013-07-25T03:56:11.883 回答
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...以更动态的方式执行此操作...我在同一个答案中发布,如果可能很快...。

不要忘记给我您的评论,或者您对这种新模式的看法。

谢谢你。

于 2013-07-25T05:58:22.250 回答