Извлечение данных XML в SQL — слишком много операторов перекрестного применения

У меня есть XML-документ, содержащий детали из заявления:

<Statement>
<Id />

<Invoices>
    <Invoice>
        <Id />
        <Date />
        <AmountDue />
        etc.
    </Invoice>

    <Invoice>
        <Id />
        <Date />
        <AmountDue />
        etc.
    </Invoice>

    <Invoice>
        <Id />
        <Date />
        <AmountDue />
        etc.
    </Invoice>
</Invoices>

</Statement>

Это отлично работает для конкретных деталей заявления:

SET @statementId = @xml.value('(Id)[1]', 'UNIQUEIDENTIFIER');

но для этого требуется синглтон и возвращает только первое значение. Мне нужны ВСЕ значения для счетов-фактур, а не только первое, поэтому синглтон не будет работать.

Я могу получить информацию, используя операторы перекрестного применения, подобные этому:

SELECT 
@statementId AS STATEMENT_ID
Id.value('.', 'uniqueidentifier') AS INVOICE_ID
Date.value('.', 'smalldatetime') AS INVOICE_DATE
Due.value('.', 'decimal') AS INVOICE_AMOUNT_DUE

FROM @xml.nodes('Statement') A(S)
cross apply S.nodes('Invoices/Invoice') B(InvoiceD)
cross apply InvoiceD.nodes('Id') C(Id)
cross apply InvoiceD.nodes('Date') D(Date)
cross apply InvoiceD.nodes('AmountDue') E(Due)

Это возвращает идентификатор, дату и сумму из каждого счета-фактуры в выписке - идеально.

Моя проблема возникает, когда я пытаюсь извлечь все детали счета. В настоящее время у меня есть семь операторов перекрестного применения, и я получил следующее сообщение:

«У процессора запросов закончились внутренние ресурсы, и он не смог создать план запроса. Это редкое событие, ожидаемое только для чрезвычайно сложных запросов или запросов, которые ссылаются на очень большое количество таблиц или разделов. Пожалуйста, упростите запрос. Если вы считаете, вы получили это сообщение по ошибке, обратитесь в службу поддержки клиентов для получения дополнительной информации».

Что я хочу сделать, так это иметь одно перекрестное применение для счета-фактуры и сузить точное поле в операторе выбора, но если я не использую '.' Я должен сделать так, чтобы оператор возвращал синглтон, и я не получаю все данные, которые мне нужны.

Я провел некоторое исследование об указании пространства имен в операторе select, но во всех примерах пространство имен устанавливается как http-адрес вместо узла в документе xml, и я еще не получил ничего, что можно было бы вернуть, используя этот подход.

Результат, который я ищу, выглядит примерно так, но с более подробной информацией о счете:

STATEMENT_ID      INVOICE_ID      INVOICE_DATE      INVOICE_AMOUNT_DUE     ...
Statement-1-Id    Invoice-1-Id    Invoice-1-Date    Invoice-1-AmountDue    ...
Statement-1-Id    Invoice-2-Id    Invoice-2-Date    Invoice-2-AmountDue    ...
Statement-1-Id    Invoice-3-Id    Invoice-3-Date    Invoice-3-AmountDue    ...

Куда мне идти отсюда?

РЕДАКТИРОВАТЬ: я удалил ненужную информацию. Моя цель здесь — получить все детали, относящиеся к счету.


person Jacob Danks    schedule 26.09.2012    source источник
comment
Как бы вы хотели, чтобы результат выглядел? Платежи и счета-фактуры являются братьями и сестрами в XML, поэтому мне трудно понять, как вы хотите, чтобы это возвращалось в одном запросе. Части в XML, где у вас есть *, немного сбивают с толку. Являются ли представленные там данные отдельными элементами в XML со значениями?   -  person Mikael Eriksson    schedule 26.09.2012
comment
Вам не нужно выполнять перекрестное применение полностью к элементу значения. Вы можете указать ID как одноэлементное значение, используя InvoiceD.value('ID[1]'. .....   -  person Mikael Eriksson    schedule 26.09.2012
comment
Спасибо за эти вопросы - я отредактировал свой пост с лучшей информацией.   -  person Jacob Danks    schedule 27.09.2012
comment
Я знаю, как указать путь в операторе выбора, когда мне нужна только одна запись, но синглтон не получит информацию обо всех счетах-фактурах, только о первом. Пожалуйста, дайте мне знать, если я неправильно понимаю некоторые из этих концепций, я новичок в этом. Спасибо!   -  person Jacob Danks    schedule 27.09.2012


Ответы (1)


select @XML.value('(Statement/Id/text())[1]', 'uniqueidentifier') as StatementId, 
       T.N.value('(Id/text())[1]', 'uniqueidentifier') as InvoiceId,
       T.N.value('(Date/text())[1]', 'smalldatetime') as InvoiceDate,
       T.N.value('(AmountDue/text())[1]', 'decimal') as AmountDue
from @XML.nodes('/Statement/Invoices/Invoice') as T(N)

.nodes разделит ваш XML на строки, чтобы каждая строка T.N указывала на собственный узел Invoice. На этом узле есть только один узел Id, поэтому выборка значения, указывающего одиночный элемент Id[1], работает.

Вы можете использовать Id[1] или (Id/text())[1], но последний даст вам более эффективный план выполнения.

person Mikael Eriksson    schedule 26.09.2012
comment
Это блестяще. Большое спасибо! это /text() заставляет его возвращать их все? - person Jacob Danks; 27.09.2012
comment
@JacobDanks Нет, это не так. Добавил немного больше пояснений к ответу. - person Mikael Eriksson; 27.09.2012