【问题标题】:How to add XML data type in a GROUP BY clause?如何在 GROUP BY 子句中添加 XML 数据类型?
【发布时间】:2011-12-23 12:52:36
【问题描述】:

我正在创建一个论坛,所以我创建了一个包含帖子的表格。其中一个字段是Body,其类型为XML。现在我想创建一个查询,返回所有帖子和每个帖子的子项数量。我正在使用聚合函数执行此操作。当我使用聚合函数时,我需要使用 group by。当我使用group by 中的字段时,我会得到以下异常:

XML 数据类型不能进行比较或排序,除非使用 IS NULL 运算符。

我该如何解决这个问题?

我的查询是:

SELECT 
    Post.PostId, Post.[Body], Count(Children.PostId)
FROM  
    dbo.Post Post, 
    dbo.Post Children 
WHERE
    Children.ParentId = Post.PostId
GROUP BY
    Post.PostId, 
    Post.[Body]

【问题讨论】:

    标签: sql sql-server sql-server-2005 tsql


    【解决方案1】:

    您可以在 CTE 中进行聚合,然后加入到该 CTE 中

    WITH Children(Cnt, ParentId)
         AS (SELECT COUNT(*),
                    ParentId
             FROM   dbo.Post
             GROUP  BY ParentId)
    SELECT P.PostId,
           P.[Body],
           ISNULL(Cnt, 0) AS Cnt
    FROM   dbo.Post P
           LEFT JOIN Children /*To include childless posts*/
             ON Children.ParentId = P.PostId
    ORDER  BY P.PostId  
    

    【讨论】:

    • 如果 XML 超过 8000 字节,这不会截断它吗?或者这在较新的 SQL Server 版本中现在不是问题了吗?
    • 我已添加查询。性能非常重要,所以我不确定投射是否正确。
    • @MattFellows - 否 varchar(max) 最多允许 2GB,与 XML 相同,尽管实际上我可能应该说 nvarchar(max),但无论如何,现在我们已经看到了查询,发布了更好的建议。
    • @KeesC.Bakker - 是的。我错过了GROUP BY,现在添加。不要在 CTE 中添加 ORDER BY,这是没有意义的。如果您想要一个订单,则需要在查询结束时进行。 ORDER BY Post.PostId
    • @KeesC.Bakker - 完成(我没有提到需要包含ISNULL
    【解决方案2】:

    您能否在不选择 XML 字段的情况下在子查询中进行分组,然后选择 XML 字段,以及加入您的子查询的其他列?

    使用您的查询,您会得到: 例如

    SELECT Post.[Body], sq.* FROM 
    (
        SELECT Post.PostId, Count(Children.PostId)
        FROM  
          dbo.Post Post, 
          dbo.Post Children 
        WHERE
          Children.ParentId = Post.PostId
        GROUP BY
          Post.PostId
    ) as sq
    INNER JOIN Post on Post.PostId= sq.PostId
    

    但是,您在这里使用的是交叉联接/ansi 联接,这不是最好的方法。更好的方法是:

    SELECT Post.[Body], sq.* FROM 
    (
        SELECT Post.PostId, Count(Children.PostId)
        FROM  
          dbo.Post Post LEFT OUTER JOIN Children on Children.ParentId = Post.PostId
        GROUP BY
          Post.PostId
    ) as sq
    INNER JOIN Post on Post.PostId= sq.PostId
    

    【讨论】:

    • 我已添加查询。我将如何实现这一目标?
    • 答案已更新 - 以及交叉连接/ANSI 连接建议
    • 这会查询Post 表两次。只需要一次。
    【解决方案3】:

    试试这个方法希望对你有帮助

    SELECT  Post.PostId, Post.[Body], Count(Children.PostId) over() as totalids
        FROM  
            dbo.Post Post, 
            dbo.Post Children 
        WHERE
            Children.ParentId = Post.PostId
    

    因为我已经工作了检查这个

     create table  xmltable  (attr1 int identity ,attr2 varchar(50),attr3  xml)
    
        insert into xmltable select 'i',(SELECT ( SELECT 'sometext' FOR XML PATH('body'),TYPE)FOR XML PATH(''),ROOT('SampleXML')) 
        union all
         select 'j',(SELECT ( SELECT 'sometext' FOR XML PATH('body'),TYPE)FOR XML PATH(''),ROOT('SampleXML'))
         union all
         select 'k',(SELECT ( SELECT 'sometext' FOR XML PATH('body'),TYPE)FOR XML PATH(''),ROOT('SampleXML'))
        union all
        select 'l',(SELECT ( SELECT 'sometext' FOR XML PATH('body'),TYPE)FOR XML PATH(''),ROOT('SampleXML'))
    
    
    create table  xmlid  (x int ,y varchar(50))
      insert into xmlid select  1,'x'
        union all
         select 1,'y'
        union all
         select 2,'z'
        union all
         select 2,'xyz1'
         union all
         select 3,'xyz2'
        union all
         select 1,'xyz3'
         select * from  xmltable
        select * from  xmlid
    

    然后创建表

    select * from  xmltable
     select * from  xmlid
     select  attr1,attr2, attr3,count(attr1) over(partition by attr2 ) as total_qtyover from  xmltable a,xmlid b
        where  a.attr1=b.x
    

    也可以看到How to aggregrate without using `GROUP BY`?

    【讨论】:

    • 好像从阿姆斯特丹飞到巴黎要飞越横滨……看起来这样会产生很多开销。
    • 啊,新代码。这个不行所有Totalids 都是1
    • @KeesC Bakker 立即尝试。
    • Wooow... 似乎比 CTE 还要快... 47% ('over') vs 53% (CTE)。
    • @KeesC Bakker 我没有根据您的查询获得匹配的 PostId 的计数?即 Children.ParentId = Post.PostId .
    【解决方案4】:

    我会将其分解为两个查询,一个用于获取 ID 和子计数,另一个用于获取尸体。执行GROUP BY 需要服务器对分组字段进行排序,因此您可能会在不需要时移动和比较大量数据(除非您有多个具有相同 ID 的帖子正文)。

    【讨论】:

    • 你能提供一个基于给定查询的例子吗?
    • 查询是一样的,只是去掉对post body的引用。然后,当您需要正文时,只需使用所需帖子的 ID 单独执行 SELECT 即可。如果要对结果进行分页,则可以执行 SELECT Post.[Body] FROM dbo.Post WHERE PostId IN (post-id-list) ,其中 post-id-list 是当前页面上的帖子 ID 列表。
    猜你喜欢
    • 2012-04-24
    • 1970-01-01
    • 2012-10-01
    • 2014-03-05
    • 1970-01-01
    • 1970-01-01
    • 2018-03-23
    • 2017-02-19
    • 1970-01-01
    相关资源
    最近更新 更多