【问题标题】:Construct nested object graph from SQL hasmany relationship从 SQL hasmany 关系构造嵌套对象图
【发布时间】:2013-09-14 14:58:30
【问题描述】:

示例:我有一些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 条件的 cmets,最后将 cmets 添加到相应的文章中。
  2. 好旧的连接。一方面,我仍然需要大量摆弄数据才能进入我想要的结构。但除此之外,我有点担心,因为每个评论都会传输像 articles.content 这样的有效负载 - 除非有办法进行我不知道的加入。

我希望我的 SQL 文盲让我错过了简单的解决方案。

【问题讨论】:

  • 我不能代表 postgresql,但 MySQL 在处理分层数据方面非常糟糕。您在此处列出的第一种方法没有任何问题。可能是我要走的路。
  • @invertedSpear:似乎是最好的方法,除非完全读取一致性很重要。由于这个问题对我来说似乎并不特别陌生,我希望一些 RDBMS 可以真正解决它。
  • 相当肯定 SQLServer 是您对分层数据的回答。我认为 TSQL 有一些很好的函数来处理它,但是根据您的应用程序 SQL 驱动程序,您可能会发现您仍在处理内容列中更大的有效负载。
  • @invertedSpear:Postgres 可以像 SQL Server 一样执行分层查询(尽管我认为这里不需要它们)——而且 Postgres 具有原生 JSON 支持。

标签: mysql sql postgresql


【解决方案1】:

您可以使用聚合和/或子查询来执行此操作。比如:

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/某事中 - 只需将其包装到另一个聚合查询中,如下所示:

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的经验。

【讨论】:

  • 您可能要补充一点,json_agg 是刚刚发布的 9.3 版本中引入的新功能。
  • @a_horse_with_no_name 可以,但在早期版本中仍然可以使用array_aggstring_agg 进行模拟。
  • 我还认为您需要json_agg(comments),因为该函数需要一个记录,而不是两个标量值。
  • @a_horse_with_no_name json_agg(comments) 将聚合comments 的所有字段。现在无法对其进行测试,但我看到了一些对json_agg 进行此类调用的示例。如果它不起作用 - 使用记录构造函数将它们组合成一条记录。
  • 谢谢,这似乎是一个可行的解决方案。似乎 MySQL 的GROUP_CONCAT 可以用作STRING_AGG,其他一切都可以在此基础上实现。
【解决方案2】:

这是一个 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

【讨论】:

    【解决方案3】:

    如果两个查询都在同一个事务中完成,则第一种方法是正确的。它基本上用服务器端的 CPU 时间(用于昂贵的第二次查询)换取客户端的一些 CPU 时间和介于两者之间的网络流量。

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

    【讨论】:

      【解决方案4】:

      PostgreSQL 的 json_agg 函数的等效函数已列入 MySQL 下一版本的议程。

      这将非常有帮助。

      您可以在(有一个提示可以实现与当前版本相同的结果):

      MySQL 8.0 Labs: JSON aggregation functions(to follow)

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2014-02-13
        • 1970-01-01
        • 2023-01-13
        • 2016-01-07
        • 2015-02-26
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多