【问题标题】:Select parent and its children with SQL Server使用 SQL Server 选择父级及其子级
【发布时间】:2012-03-06 02:30:38
【问题描述】:

我将从结尾开始:我想要得到如下所示的 JSON:

"Message": "Hello World",
"Comments": [
            {
               "text": "Comment 1",
            },
            {
               "text": "Comment 2"
            }
         ],

要获取 JSON,我会将结果作为“消息”并在其上调用此函数。

System.Web.Script.Serialization.JavaScriptSerializer oSerializer =
new System.Web.Script.Serialization.JavaScriptSerializer();
string sJSON = oSerializer.Serialize(messages);

我想知道的是如何像这样从数据库中获取数据,以便 Comments 位于子集合中,就像您在上面的 JSON 中看到的那样。

我有 2 个表,第一个表保存消息,另一个保存 cmets。 cmets 表中的每个注释都有一个带有messageID 的外键。

目前,我使用 2 个 SQL 查询来执行此操作 - 一个用于消息,第二个用于每条消息 - 我只调用 GetCommentByID 查询。这 2 个 SQL 查询很浪费,因为它的复杂度是 O(N^2)。

我想知道我是否可以通过单个 SQL 查询来实现这一点。

我只是不确定 SQL Server 是否能够返回子数据集或数组。

我也在这里阅读了这个问题: mysql: select the last 10 messages and for each message the last 3 replies

似乎唯一的方法是获取一个包含所有消息和 cmets 的数据集,然后在服务器上对其进行操作。

这是我的表的架构:

消息:

ID, Description

评论:

ID, MessageID, Body

我可以通过单个 SQL 查询来实现吗?

【问题讨论】:

    标签: sql sql-server


    【解决方案1】:

    不确定这个操作的 O(n^2) 是什么 - 该操作将使用索引查找,在最坏的情况下,在 n 的表大小上将是 O(log n),您将获得 10 行然后再寻找 10 个类似的 cmets - 是的,如果您为 m 条消息设置 1 组,为每条消息的 cmets 设置 m 组,则为 m + 1 次查询。

    但无论如何,您要求在一次操作中完成。我不确定 JSON 的来源,所以我继续并简单地将您想要的完整 JSON 作为单行中的单列返回。

    1. 这个 JSON 不一定是很好的转义
    2. 您始终可以只加入两个表(单个查询)并按程序处理集合,当 Message.ID 更改时中断(因为 SQL Server 中没有内置 JSON 库,我假设您无论如何都会这样做) .唯一的浪费是所有消息数据在每一行上都不必要地重复了。

    Link to a Runnable example

    SET NOCOUNT ON;
    DECLARE @Messages AS TABLE (ID INT NOT NULL, Description VARCHAR(MAX) NOT NULL);
    DECLARE @Comments AS TABLE (ID INT NOT NULL, MessageID INT NOT NULL, Body VARCHAR(MAX) NOT NULL);
    
    INSERT INTO @Messages
    VALUES (1, 'Message 1'),
    (2, 'Message 2'),
    (3, 'Message 3'),
    (4, 'Message 4'),
    (5, 'Message 5'),
    (6, 'Message 6'),
    (7, 'Message 7'),
    (8, 'Message 8'),
    (9, 'Message 9'),
    (10, 'Message 10'),
    (11, 'Message 11'),
    (12, 'Message 12'),
    (13, 'Message 13');
    
    INSERT INTO @Comments
    SELECT ROW_NUMBER() OVER(ORDER BY msg.ID, Numbers.num)
    , msg.ID
    , msg.Description + ':Commment ' + CONVERT(varchar(max), Numbers.num)
    FROM @Messages msg
    CROSS JOIN (SELECT 1 AS num UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5) AS Numbers;
    
    -- SELECT * FROM @Messages;
    -- SELECT * FROM @Comments;
    
    SELECT '[ ' + STUFF(Z.Messages, 1, 2, '') + ' ] ' AS JSON
    FROM (
    SELECT ', ' + MessageWComments FROM (
    SELECT TOP 10 '{ "Message": "' + msg.Description + '", "Comments": [ ' + STUFF(X.Comments, 1, 2, '') + ' ] }' AS MessageWComments
    FROM @Messages msg
    OUTER APPLY (
        SELECT TOP 3 ', { "text" : "' + Body + '" }'
        FROM @Comments cmt
        WHERE MessageID = msg.ID
        ORDER BY cmt.ID DESC
        FOR XML PATH ('')
        ) AS X (Comments)
    ORDER BY msg.ID DESC
    ) AS Y (MessageWComments)
    FOR XML PATH ('')) AS Z(Messages)
    

    【讨论】:

    • 谢谢。我将通过 c# 代码获取 JSON。那不是重要的部分。我只是想获取子数据集中的组合数据
    【解决方案2】:

    按消息分隔的注释字符串连接可能是可行的方法(SQL Server 2005 或更高版本)。

    select distinct
    [Message]=m.[Description]
    , Comments=(STUFF((SELECT ',' + c1.Body FROM Comments c1 WHERE  c.MessageID = c1.MessageID ORDER BY c.ID
    FOR XML PATH(''), TYPE, ROOT).value('root[1]','nvarchar(max)'),1,1,''))
    from [Messages] m
    inner join Comments c on c.MessageID=m.ID;
    

    根据您之前的评论,我将省略 C# 格式,只返回基本聚合。

    【讨论】:

      猜你喜欢
      • 2014-02-09
      • 1970-01-01
      • 1970-01-01
      • 2011-03-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-12-18
      相关资源
      最近更新 更多