使用SQL Server选择父级及其子级

时间:2012-03-06 02:30:39

标签: sql sql-server

我将从最后开始:我想获得看起来像这样的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个表,第一个表保存消息,另一个表保存注释。 注释表中的每个注释都有一个带有messageID的外键。

目前,我正在使用2个SQL查询 - 一个用于消息,第二个用于每个消息 - 我只是调用GetCommentByID查询。这两个SQL查询是浪费的,因为它的复杂性是O(N ^ 2)。

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

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

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

并且似乎唯一的方法是将一个包含所有消息和注释的数据集放在一起,然后在服务器上对其进行操作。

这是我的表的架构:

讯息:

ID, Description

注释:

ID, MessageID, Body

我可以通过单一SQL查询来实现这一目标吗?

2 个答案:

答案 0 :(得分:1)

不确定关于此操作的O(n ^ 2)是什么 - 操作将使用索引搜索,在最坏的情况下,在n的表大小上将是O(log n),并且您将获得10行那个,然后另外10个类似的寻求评论 - 是的,如果你有m个消息的1个集合,并且每个消息的评论有更多的话,那么它是m + 1个查询。

但无论如何,你要求在一次手术中做到这一点。我不确定JSON的位置,所以我继续前进,只需将您想要的完整JSON作为一行中的单个列返回。

  1. 这个JSON不一定是好转的
  2. 你总是可以加入这两个表(单个查询)并按程序处理集合,在Message.ID发生变化时中断(因为SQL Server中没有内置的JSON库,我认为你无论如何都会这样做) 。这里唯一的浪费是所有消息数据在网络上不必要地重复每一行。
  3. 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)
    

答案 1 :(得分:0)

按消息分隔注释字符串的连接可能是要走的路(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#格式,只返回基本聚合。