使用节点()和值()获取父值

时间:2011-09-15 17:04:51

标签: sql sql-server xml sql-server-2008 xquery

我有一个与此XSD匹配的XML文档代码段:

<xs:complexType name="QuestionType">
  <xs:sequence>
    <xs:element name="questionId" type="xs:string" minOccurs="1" />
    <xs:element name="questionDescription" type="xs:string" minOccurs="1" />
    <xs:element name="questionHeader" type="xs:string" minOccurs="0" />
    <xs:element name="questionLabel" type="xs:string" minOccurs="0" />
    <xs:element name="version" type="xs:string" minOccurs="1" maxOccurs="1" />
    <xs:element name="SubQuestion" type="QuestionType"
                minOccurs="0" maxOccurs="unbounded" />
  </xs:sequence>
</xs:complexType>

这递归地定义了一个<Question>个元素,这些元素可以包含<SubQuestion>个元素,但都属于QuestionType类型。

使用SQL,我想查询一次文档以获得包含所有问题和子问题的单个结果集。我目前有两个独立的查询来实现这一点(请注意我只使用NVarChar(1000)进行测试 - 它们在生产中的尺寸会更合适,@X是一个与上面的模式匹配的XML变量):

SELECT -- Top-level questions...
  C.value('questionId[1]', 'NVarChar(1000)') Id,
  NULL ParentId,
  C.value('questionDescription[1]', 'NVarChar(1000)') Description,
  NULLIF(C.value('questionHeader[1]', 'NVarChar(1000)'), '') Header,
  NULLIF(C.value('questionLabel[1]', 'NVarChar(1000)'), '') Label,
  C.value('version[1]', 'NVarChar(1000)') Version
FROM @X.nodes('//Question') X(C);

SELECT -- Sub-questions...
  C.value('questionId[1]', 'NVarChar(1000)') Id,
  C.query('..').value('(Question/questionId)[1]', 'NVarChar(1000)') ParentId,
  C.value('questionDescription[1]', 'NVarChar(1000)') Description,
  NULLIF(C.value('questionHeader[1]', 'NVarChar(1000)'), '') Header,
  NULLIF(C.value('questionLabel[1]', 'NVarChar(1000)'), '') Label,
  C.value('version[1]', 'NVarChar(1000)') Version
FROM @X.nodes('//SubQuestion') X(C);

我希望这可以通过递归CTE来解决,但是我把它放在一起很麻烦。

3 个答案:

答案 0 :(得分:1)

到目前为止,我正在这样做,虽然我仍然希望稍微压缩查询:

WITH Q AS (
  SELECT
    C.value('questionId[1]', 'NVarChar(1000)') Id,
    NULL ParentId,
    C.value('questionDescription[1]', 'NVarChar(1000)') Description,
    NULLIF(C.value('questionHeader[1]', 'NVarChar(1000)'), '') Header,
    NULLIF(C.value('questionLabel[1]', 'NVarChar(1000)'), '') Label,
    C.value('version[1]', 'NVarChar(1000)') Version
  FROM @X.nodes('//Question') X(C)
  UNION ALL
  SELECT
    C.value('questionId[1]', 'NVarChar(1000)') Id,
    COALESCE(
      C.query('..').value('(Question/questionId)[1]', 'NVarChar(1000)'),
      C.query('..').value('(SubQuestion/questionId)[1]', 'NVarChar(1000)')
    ) ParentId,
    C.value('questionDescription[1]', 'NVarChar(1000)') Description,
    NULLIF(C.value('questionHeader[1]', 'NVarChar(1000)'), '') Header,
    NULLIF(C.value('questionLabel[1]', 'NVarChar(1000)'), '') Label,
    C.value('version[1]', 'NVarChar(1000)') Version
  FROM @X.nodes('//SubQuestion') X(C)
)
SELECT Q.Id, Q.ParentId, Q.Description, Q.Header, Q.Label, Q.Version
FROM Q;

这是重要的一点,因为它将获得第一个非空ParentId值中的任何一个:

COALESCE(
  C.query('..').value('(Question/questionId)[1]', 'NVarChar(1000)'),
  C.query('..').value('(SubQuestion/questionId)[1]', 'NVarChar(1000)')
) ParentId

答案 1 :(得分:1)

鉴于您已使用sql-server-2008标记了此问题,并且IMHO SQL Server 2008支持XQuery,我想建议一个不同的“角度”:使用XPath表达式选择您感兴趣的节点。

.//*[local-name(.) = 'Question' or local-name(.) = 'SubQuestion']

请注意,我正在使用XPath函数local-name(),以防您的真实XML数据具有名称空间声明。

我创建了一个示例XML文件来测试上面的表达式:

<?xml version="1.0" encoding="UTF-8"?>
<Questions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns="http://www.acme.com" xsi:schemaLocation="sample.xsd">
    <Question>
        <questionId>1</questionId>
        <questionDescription>Question 1</questionDescription>
        <version>1</version>
        <SubQuestion>
            <questionId>1.1</questionId>
            <questionDescription>Question 1.1</questionDescription>
            <version>1</version>
            <SubQuestion>
                <questionId>1.1.1</questionId>
                <questionDescription>Question 1.1.1</questionDescription>
                <version>1</version>
                <SubQuestion>
                    <questionId>1.1.1.1</questionId>
                    <questionDescription>Question 1.1.1.1</questionDescription>
                    <version>1</version>
                </SubQuestion>
                <SubQuestion>
                    <questionId>1.1.1.2</questionId>
                    <questionDescription>Question 1.1.1.2</questionDescription>
                    <version>1</version>
                </SubQuestion>
            </SubQuestion>
            <SubQuestion>
                <questionId>1.2</questionId>
                <questionDescription>Question 1.2</questionDescription>
            </SubQuestion>
        </SubQuestion>
    </Question>
    <Question>
        <questionId>2</questionId>
        <questionDescription>Question 2</questionDescription>
        <version>1</version>
    </Question>
    <Question>
        <questionId>3</questionId>
        <questionDescription>Question 3</questionDescription>
        <version>1</version>
        <SubQuestion>
            <questionId>3.1</questionId>
            <questionDescription>Question 3.1</questionDescription>
            <version>1</version>
        </SubQuestion>
    </Question>
</Questions>

针对该示例评估此XQuery查询

declare namespace acme = "http://www.acme.com";
<AllQuestions>
 {
   for $question in .//*[local-name(.) = 'Question' or local-name(.) = 'SubQuestion']
   return
        <Question>
            <questionId>{ data($question/acme:questionId) }</questionId>
            <questionDescription>{ data($question/acme:questionDescription) }</questionDescription>
        </Question>
 }
</AllQuestions>

将导致

<?xml version="1.0" encoding="UTF-8"?>
<AllQuestions>
   <Question>
      <questionId>1</questionId>
      <questionDescription>Question 1</questionDescription>
   </Question>
   <Question>
      <questionId>1.1</questionId>
      <questionDescription>Question 1.1</questionDescription>
   </Question>
   <Question>
      <questionId>1.1.1</questionId>
      <questionDescription>Question 1.1.1</questionDescription>
   </Question>
   <Question>
      <questionId>1.1.1.1</questionId>
      <questionDescription>Question 1.1.1.1</questionDescription>
   </Question>
   <Question>
      <questionId>1.1.1.2</questionId>
      <questionDescription>Question 1.1.1.2</questionDescription>
   </Question>
   <Question>
      <questionId>1.2</questionId>
      <questionDescription>Question 1.2</questionDescription>
   </Question>
   <Question>
      <questionId>2</questionId>
      <questionDescription>Question 2</questionDescription>
   </Question>
   <Question>
      <questionId>3</questionId>
      <questionDescription>Question 3</questionDescription>
   </Question>
   <Question>
      <questionId>3.1</questionId>
      <questionDescription>Question 3.1</questionDescription>
   </Question>
</AllQuestions>

编辑 - 最终查询

SELECT
    C.value('questionId[1]', 'NVarChar(1000)') Id,
    COALESCE(
      C.query('..').value('(Question/questionId)[1]', 'NVarChar(1000)'),
      C.query('..').value('(SubQuestion/questionId)[1]', 'NVarChar(1000)')
    ) ParentId,
    C.value('questionDescription[1]', 'NVarChar(1000)') Description,
    NULLIF(C.value('questionHeader[1]', 'NVarChar(1000)'), '') Header,
    NULLIF(C.value('questionLabel[1]', 'NVarChar(1000)'), '') Label,
    C.value('version[1]', 'NVarChar(1000)') Version
FROM
  @X.nodes('.//*[local-name(.)="Question" or local-name(.)="SubQuestion"]') X(C);

答案 2 :(得分:0)

您可以使用CTE:

WITH TopLevel (ID, ParentID, Description, Header, Label,Level)
AS
(
SELECT -- Top-level questions...
  C.value('questionId[1]', 'NVarChar(1000)') Id,
  NULL ParentId,
  C.value('questionDescription[1]', 'NVarChar(1000)') Description,
  NULLIF(C.value('questionHeader[1]', 'NVarChar(1000)'), '') Header,
  NULLIF(C.value('questionLabel[1]', 'NVarChar(1000)'), '') Label,
  C.value('version[1]', 'NVarChar(1000)') Version,
  0 AS Level
FROM @X.nodes('//Question') X(C)
UNION ALL
SELECT -- Sub-questions...
  C.value('questionId[1]', 'NVarChar(1000)') Id,
  C.query('..').value('(Question/questionId)[1]', 'NVarChar(1000)') ParentId,
  C.value('questionDescription[1]', 'NVarChar(1000)') Description,
  NULLIF(C.value('questionHeader[1]', 'NVarChar(1000)'), '') Header,
  NULLIF(C.value('questionLabel[1]', 'NVarChar(1000)'), '') Label,
  C.value('version[1]', 'NVarChar(1000)') Version
  ,Level + 1 AS Level
FROM @X.nodes('//SubQuestion') X(C)
JOIN TopLevel AS t ON C.query('..').value('(Question/questionId)[1]', 'NVarChar(1000)') = t.id )

SELECT * FROM TopLevel

参考:http://msdn.microsoft.com/en-us/library/ms186243.aspx