从SQL hasmany关系构造嵌套对象图

时间:2013-09-10 18:52:39

标签: mysql sql postgresql

示例:我有一些articlescomments,我希望得到这样的内容:

[{
   title: "Article 1",
   content: "Super long article goes here",
   comments: [
      { author: "Troll", message: "You suck, Sir!" }, 
      { author: "SpamBot", message: "http://superawesomething.com/"}
   ]
},{
   title: "Article 2",
   content: "Another long article goes here",
   comments: [ ... ]
}]

现在我看到两个解决方案:

  1. 首先获取文章,然后在第二个查询中添加一些IN条件的评论,最后将评论添加到相应的文章中。
  2. 好老加入。对于其中一个,我仍然需要调整大量数据才能进入我想要的结构。但除此之外我有点担心,因为每个评论都会传输像articles.content这样的有效负载 - 除非有办法进行加入我不知道。
  3. 我希望我的SQL文盲能让我错过这个简单的解决方案。

4 个答案:

答案 0 :(得分:6)

您可以使用聚合和/或子查询来执行此操作。类似的东西:

select title, content, json_agg(comments.author, comments.message) as comments
from articles 
join comments on articles.article_id = comments.article_id
group by article_id;

如果你需要将它聚合成一个字符串/ json / something - 只需将它包装到另一个聚合查询中,如下所示:

select json_agg(sub)
from (
  select title, content, json_agg(comments.author, comments.message) as comments
  from articles 
  join comments on articles.article_id = comments.article_id
  group by article_id) sub;

这是一个Postgres查询。没有Mysql的经验。

答案 1 :(得分:1)

这是一个MySQL解决方案:

SELECT CONCAT( '[ { '
              ,GROUP_CONCAT( CONCAT( 'title: "', REPLACE( a.title, '"', '\"' ), '"'
                                    ,', contents: "', REPLACE( a.content, '"', '\"' ), '"'
                                    ,', comments: ', a.comments
                                   )
                             SEPARATOR ' }, { '
                           )
              ,' } ]'
             )
  FROM (SELECT a1.title
              ,a1.content
              ,CONCAT( '[ { '
                      ,GROUP_CONCAT( CONCAT( 'author: "', REPLACE( c.author, '"', '\"' ), '"'
                                            ,', message: "', REPLACE( c.message, '"', '\"' ), '"'
                                           )
                                     SEPARATOR ' }, { '
                                   )
                      ,' } ]'
                     ) as comments
          FROM articles a1
          LEFT OUTER
          JOIN comments c
            ON c.articleId = a1.articleId
         GROUP BY a1.title, a1.content
       ) a
;

当字符串变大时,需要进行一些调整。最好每篇文章返回一行:

SELECT a1.title
      ,a1.content
      ,CONCAT( '[ { '
              ,GROUP_CONCAT( CONCAT( 'author: "', REPLACE( c.author, '"', '\"' ), '"'
                                    ,', message: "', REPLACE( c.message, '"', '\"' ), '"'
                                   )
                             SEPARATOR ' }, { '
                           )
              ,' } ]'
             ) as comments
  FROM articles a1
  LEFT OUTER
  JOIN comments c
    ON c.articleId = a1.articleId
 GROUP BY a1.title, a1.content

SQLFiddle:http://sqlfiddle.com/#!2/5edcd/13

答案 2 :(得分:0)

如果两个查询都在同一个事务中完成,则第一种方法是正确的。它主要在服务器端交换CPU时间(对于代价高昂的第二个查询),在客户端的某些CPU时间和两者之间的网络流量。

虽然第一种方法最初在网络流量方面看起来很昂贵,但是一些在线协议支持以有效的方式引用相同值的列。在这种情况下,不会为每一行传输由于连接而重复的列值。您应该检查实际产生的流量,开销可能没有看起来那么大 在客户端,您可以使用articles的主键将所有article存储在类似hashmap的对象中。在接收行时,您会向列表添加注释,每个注释都会被该键索引。通过根据article的主键对服务器端的行进行排序,可以提高效率,使您能够在客户端上使用自然排序的列表列表。与第一种方法相比,构建这种结构是cpu-time-tradeoff 我的判决:使用JOIN

答案 3 :(得分:0)

PostgreSQL的json_agg功能的等效功能已列入MySQL的下一个版本的议程。

这将非常有帮助。

您可以查看(有提示与当前版本实现相同类型的结果):

MySQL 8.0 Labs: JSON aggregation functions(to follow)