【问题标题】:SQL Server 2005 "FOR XML PATH" GroupingSQL Server 2005“FOR XML PATH”分组
【发布时间】:2009-09-18 09:01:19
【问题描述】:

我正在尝试使用“FOR XML”构造从 SQL Server 2005 数据库生成 XML 文档。

数据库中有两个简单的表,一对多的关系:

1) 杂志

 | Id | Number | Name       |
 ----------------------------
 | 53 | 0001   | Magazine 1 |
 | 54 | 0002   | Magazine 2 |
 | 55 | 0003   | Magazine 3 |

2) 文章

 | Id |   Title   | MagazineId | Size |
 --------------------------------------
 | 1  | Article 1 |        53  | 1205 |
 | 2  | Article 2 |        53  | 817  |
 | 3  | Article 3 |        54  | 1570 |
 | 4  | Article 4 |        54  | 2510 |
 | 5  | Article 5 |        55  | 910  |

假设我必须找到文章大小大于 1000 的所有杂志 并生成以下 xml:

<Magazines>
    <Magazine Id="53">
      <Number>0001</Number>
      <Articles>
        <Article Id="1">
           <Title>Article 1</Title>
           <Size>1205</Size>
        </Article>
      </Articles>
    </Magazine>
    <Magazine Id="54">
      <Number>0002</Number>
      <Articles>
        <Article Id="3">
          <Title>Article 3</Title>
          <Size>1570</Size>
        </Article>
        <Article Id="4">
          <Title>Article 4</Title>
          <Size>2510</Size>
        </Article>
      </Articles>
    </Magazine>
</Magazines>

我正在尝试使用“PATH”模式生成这样的 xml:

SELECT Magazines.Id AS "@Id",
       Magazines.Number AS "Number",
       Articles.Id AS "Articles/Article/@Id",
       Articles.Title AS "Articles/Article/Title",
       Articles.Size AS "Articles/Article/Size"
FROM Magazines INNER JOIN Articles ON Magazines.Id = Articles.MagazineId
WHERE Articles.Size > 1000
FOR XML PATH('Magazine'), ROOT('Magazines'), TYPE

它将产生以下xml:

<Magazines>
  <Magazine Id="53">
    <Number>0001</Number>
    <Articles>
      <Article Id="1">
        <Title>Article 1</Title>
        <Size>1205</Size>
      </Article>
    </Articles>
  </Magazine>
  <Magazine Id="54">
    <Number>0002</Number>
    <Articles>
      <Article Id="3">
        <Title>Article 3</Title>
        <Size>1570</Size>
      </Article>
    </Articles>
  </Magazine>
  <Magazine Id="54">
    <Number>0002</Number>
    <Articles>
      <Article Id="4">
        <Title>Article 4</Title>
        <Size>2510</Size>
      </Article>
    </Articles>
  </Magazine>
</Magazines>

因此,Id="54" 的杂志有两个元素(每篇文章一个),这就是问题所在。

我可以使用这样的子查询重写查询:

SELECT M.Id AS "@Id",
       M.Number AS "Number",
    (SELECT Articles.Id AS "@Id",
               Articles.Title AS "Title",
               Articles.Size AS "Size"
  FROM Articles
  WHERE Articles.MagazineId = M.Id
  FOR XML PATH('Article'), ROOT('Articles'), TYPE
    )
FROM Magazines AS M 
FOR XML PATH('Magazine'), ROOT('Magazines'), TYPE

这会产生以下 xml:

<Magazines>
  <Magazine Id="53">
    <Number>0001</Number>
    <Articles>
      <Article Id="1">
        <Title>Article 1</Title>
        <Size>1205</Size>
      </Article>
      <Article Id="2">
        <Title>Article 2</Title>
        <Size>817</Size>
      </Article>
    </Articles>
  </Magazine>
  <Magazine Id="54">
    <Number>0002</Number>
    <Articles>
      <Article Id="3">
        <Title>Article 3</Title>
        <Size>1570</Size>
      </Article>
      <Article Id="4">
        <Title>Article 4</Title>
        <Size>2510</Size>
      </Article>
    </Articles>
  </Magazine>
  <Magazine Id="55">
    <Number>0003</Number>
    <Articles>
      <Article Id="5">
        <Title>Article 5</Title>
        <Size>910</Size>
      </Article>
    </Articles>
  </Magazine>
</Magazines>

但是通过使用子查询,我无法按文章列过滤杂志(没有复杂的附加查询)。

“FOR XML AUTO”模式不适合,因为它很简单,不支持一些“PATH”特性(比如属性使用@、ROOT等)

那么,“PATH”模式是否有可能像“AUTO”模式一样对内表数据进行分组?

谢谢!

【问题讨论】:

    标签: sql-server-2005 for-xml


    【解决方案1】:

    好吧,您可以通过在子查询中指定“size > 1000”来更进一步:

    SELECT M.Id AS "@Id",
           M.Number AS "Number",
        (SELECT Articles.Id AS "@Id",
                   Articles.Title AS "Title",
                   Articles.Size AS "Size"
         FROM Articles
         WHERE Articles.MagazineId = M.Id
               AND Articles.Size > 1000
         FOR XML PATH('Article'), ROOT('Articles'), TYPE
        )
    FROM Magazines AS M 
    FOR XML PATH('Magazine'), ROOT('Magazines'), TYPE
    

    您现在缺少的是,您仍然会得到没有文章大小 > 1000 的杂志。您可以像这样消除这些:

    SELECT M.Id AS "@Id",
           M.Number AS "Number",
        (SELECT Articles.Id AS "@Id",
                   Articles.Title AS "Title",
                   Articles.Size AS "Size"
          FROM Articles
          WHERE Articles.MagazineId = M.Id
                AND Articles.Size > 1000
          FOR XML PATH('Article'), ROOT('Articles'), TYPE
        )
    FROM Magazines AS M 
    WHERE EXISTS(SELECT * FROM Articles 
                 WHERE Articles.MagazineId = M.Id 
                   AND Articles.Size > 1000)
    FOR XML PATH('Magazine'), ROOT('Magazines'), TYPE
    

    (未经测试,我现在手头没有 SQL 服务器)。

    这对你有用吗?它会为您提供您正在寻找的杂志和文章吗?

    马克

    【讨论】:

    • 嗨,马克!感谢您的帮助!您的查询工作得很好,但不幸的是,子查询会导致 SQL 端出现一些性能问题。所以,我们决定在应用服务器端构建xml。还是谢谢!
    【解决方案2】:

    使用 FOR XML 显式。编写最长的代码,但消除子查询引发的性能问题。

    【讨论】:

      猜你喜欢
      • 2011-01-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-11-04
      • 2017-12-06
      • 1970-01-01
      相关资源
      最近更新 更多