在有关资源、主题和章节的规范化数据库中使用 GROUP BY 加入

JOIN with GROUP BY in a Normalized DB about Resources, Topics & Chapters

我已经规范化了我的数据库,但似乎无法 return 我正在寻找的数据以正确的方式。

我有 5 个 tables:

  1. 资源(5 个资源)
  2. 主题(10 个主题)
  3. 章节(10 章)
  4. 主题到资源(18 个主题到资源链接)
  5. 主题到章节(18 个主题到章节的链接)

看看这个 SQL Fiddle...

我需要收集资源 table 中的所有记录并将它们与相应的主题和章节分组(从主题到资源和主题到章节 table s)

任何人都可以建议正确的 SQL 查询 return 5 个资源记录及其参加的主题和章节吗?

我已经尝试使用 GROUP BY 进行 JOINS,这将记录集压缩为 5 个资源,但没有包含我需要的所有其他信息(主题和章节)。

SELECT * FROM TOPICS, CHAPTERS, RESOURCES AS RES
INNER JOIN TOPICS_to_RESOURCE AS TR ON RES.RES_ID = TR.TR_RESID
INNER JOIN TOPICS_to_CHAPTER AS TCH ON TR.TR_TID = TCH.TCH_TID
GROUP BY RES.RES_ID

我无法真正区分您想要实现的目标,但听起来您只是想获得一份 table 来显示每一章的主题和资源。

如果是,则以下SQL:

select * from resources r
JOIN topics_to_resource ttr ON ttr.tr_resid = r.res_id
JOIN topics t on t.t_id = ttr.tr_tid
JOIN topics_to_chapter ttc on ttc.tch_tid = t.t_id
JOIN chapters ch ON ch.ch_id = tch_chid
ORDER BY r.res_id;

将 return 只是那样,根据 http://sqlfiddle.com/#!9/ddf252/12

或者,忽略 select 中的加入 ID:

select r.res_id, r.res_name, t.t_id, t.t_name, ch.ch_id, ch.ch_name from resources r
JOIN topics_to_resource ttr ON ttr.tr_resid = r.res_id
JOIN topics t on t.t_id = ttr.tr_tid
JOIN topics_to_chapter ttc on ttc.tch_tid = t.t_id
JOIN chapters ch ON ch.ch_id = tch_chid
ORDER BY r.res_id, t.t_id, ch.ch_id

根据http://sqlfiddle.com/#!9/ddf252/14

如果这不是您想要的,能否详细说明一下您希望看到的结果?

编辑:return一个包含所有相关记录的更简洁的列表

select 
CONCAT(r.res_id,': ',r.res_name) 'Resources', 
GROUP_CONCAT(CONCAT(' (',t.t_id,': ',t.t_name,')')) 'Topics', 
GROUP_CONCAT(CONCAT(' (',ch.ch_id,': ',ch.ch_name,')')) 'Chapters'
from resources r
JOIN topics_to_resource ttr ON ttr.tr_resid = r.res_id
JOIN topics t on t.t_id = ttr.tr_tid
JOIN topics_to_chapter ttc on ttc.tch_tid = t.t_id
JOIN chapters ch ON ch.ch_id = tch_chid
GROUP BY r.res_id
ORDER BY r.res_id, t.t_id, ch.ch_id

根据http://sqlfiddle.com/#!9/ddf252/30

最后,按章节和主题分组:

select 
CONCAT(res_id,': ',res_name) 'Resources', 
GROUP_CONCAT(`chapters` order by chapters separator '\n') as 'Content'
FROM
  (SELECT r.res_id 'res_id',
          r.res_name 'res_name', 
          t.t_id 't_id',
          t.t_name 't_name',
          CONCAT(t.t_name,': (',GROUP_CONCAT(ch.ch_name ORDER BY t.t_name separator ','),')') 'Chapters'
    FROM resources r
      JOIN topics_to_resource ttr ON ttr.tr_resid = r.res_id
      JOIN topics t on t.t_id = ttr.tr_tid
      JOIN topics_to_chapter ttc on ttc.tch_tid = t.t_id
      JOIN chapters ch ON ch.ch_id = tch_chid
    GROUP BY res_id, t_id
    ORDER BY r.res_id, t.t_id, ch.ch_id) as t
GROUP BY res_id

如此处所示:http://sqlfiddle.com/#!9/ddf252/85

我检查了结果,它们看起来不错 - 但仔细检查,因为它有点像 MySQL 我脑海中的盗梦空间(这里已经凌晨 1 点了)

进一步补充:每个资源的不同值

    select CONCAT(r.res_id,': ',r.res_name) 'Resources', GROUP_CONCAT(distinct t_name separator ',') 'Topics', 
GROUP_CONCAT(distinct ch.ch_name separator ',') 'Chapters'
from resources r
JOIN topics_to_resource ttr ON ttr.tr_resid = r.res_id
JOIN topics t on t.t_id = ttr.tr_tid
JOIN topics_to_chapter ttc on ttc.tch_tid = t.t_id
JOIN chapters ch ON ch.ch_id = tch_chid
GROUP BY r.res_id
ORDER BY r.res_id, t.t_id, ch.ch_id

http://sqlfiddle.com/#!9/ddf252/88

这是非标准语法:

SELECT *
...
GROUP BY RES.RES_ID

此处您要求每一列 (select *) 但在分组依据下仅指定了一列。只有 MySQL 允许这种由服务器设置控制的非标准 GROUP BY 语法。这些默认设置最近已更改,您可能会发现我们使用非标准 GROUP BY 的许多查询将来可能会失败。

符合标准的 GROUP BY 子句指定所有 "non-aggregating" 列。这意味着您必须指定 不使用 SUM/COUNT/AVG/MIN/MAX 等的每一列


SELECT * FROM TOPICS, CHAPTERS, RESOURCES AS RES
INNER JOIN TOPICS_to_RESOURCE AS TR ON RES.RES_ID = TR.TR_RESID
INNER JOIN TOPICS_to_CHAPTER AS TCH ON TR.TR_TID = TCH.TCH_TID
GROUP BY RES.RES_ID

该查询有 2 种形式的连接语法。 **从 t1、t2、t3 ** 是古老且不明智的,不是好的做法。还;永远不要 "combine" 在单个查询中使用较新的连接语法的旧语法形式,因为这会导致查询错误。

只是不要在 from 子句中的表之间使用逗号这个简单的步骤将使您始终使用更好的语法。

顺便说一句 FROM TOPICS, CHAPTERS, RESOURCES AS RES 在 where 子句中没有任何限制将产生:

将 TOPICS 中的每一行乘以 CHAPTERS 中的每一行再乘以 RESOURCES 中的每一行。换句话说 "Cartesian product"。在更新的语法中,您的查询转换为:

SELECT * FROM TOPICS
CROSS JOIN CHAPTERS
CROSS JOIN RESOURCES AS RES
INNER JOIN TOPICS_to_RESOURCE AS TR ON RES.RES_ID = TR.TR_RESID
INNER JOIN TOPICS_to_CHAPTER AS TCH ON TR.TR_TID = TCH.TCH_TID
GROUP BY RES.RES_ID