Получить родительское значение, используя Nodes () и Value () - PullRequest
1 голос
/ 15 сентября 2011

У меня есть фрагмент XML-документа, который соответствует этому XSD:

<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 ]

1 голос
/ 15 сентября 2011

Пока я делаю это, хотя все еще надеюсь немного сузить запрос:

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 голос
/ 18 сентября 2011

Учитывая, что вы пометили этот вопрос 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);
0 голосов
/ 15 сентября 2011

Вы можете использовать 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

...