Используя SQL Server 2005 XQuery, выберите все узлы с определенным значением атрибута или с отсутствующим атрибутом - PullRequest
7 голосов
/ 18 сентября 2008

Обновление: приведение гораздо более тщательного примера.

Первые два предложенных решения соответствовали тому, что я пытался сказать , а не . Я не могу знать местоположение, он должен иметь возможность просматривать все дерево документов. Таким образом, решение в этом направлении с / Books /, указанным в качестве контекста, не будет работать:

SELECT x.query('.') FROM @xml.nodes('/Books/*[not(@ID) or @ID = 5]') x1(x)

Оригинальный вопрос с лучшим примером:

Используя реализацию SQL Server 2005 XQuery, мне нужно выбрать все узлы в документе XML, по одному разу, и сохранить их исходную структуру, но только если у них отсутствует определенный атрибут или этот атрибут имеет определенное значение (переданное параметр). Запрос также должен работать со всем документом XML (по оси «потомок или сам»), а не с выбранной глубиной.

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

Например:

Если бы это был XML:

    DECLARE @Xml XML
    SET @Xml =
    N'
<Library>
  <Novels>
    <Novel category="1">Novel1</Novel>
    <Novel category="2">Novel2</Novel>
    <Novel>Novel3</Novel>
    <Novel category="4">Novel4</Novel>
  </Novels>
  <Encyclopedias>
    <Encyclopedia>
      <Volume>A-F</Volume>
      <Volume category="2">G-L</Volume>
      <Volume category="3">M-S</Volume>
      <Volume category="4">T-Z</Volume>
    </Encyclopedia>
  </Encyclopedias>
  <Dictionaries category="1">
    <Dictionary>Webster</Dictionary>
    <Dictionary>Oxford</Dictionary>
  </Dictionaries>
</Library>
    '

Параметр 1 для категории приведет к этому:

<Library>
  <Novels>
    <Novel category="1">Novel1</Novel>
    <Novel>Novel3</Novel>
  </Novels>
  <Encyclopedias>
    <Encyclopedia>
      <Volume>A-F</Volume>
    </Encyclopedia>
  </Encyclopedias>
  <Dictionaries category="1">
    <Dictionary>Webster</Dictionary>
    <Dictionary>Oxford</Dictionary>
  </Dictionaries>
</Library>

Параметр 2 для категории приведет к этому:

<Library>
  <Novels>
    <Novel category="2">Novel2</Novel>
    <Novel>Novel3</Novel>
  </Novels>
  <Encyclopedias>
    <Encyclopedia>
      <Volume>A-F</Volume>
      <Volume category="2">G-L</Volume>
    </Encyclopedia>
  </Encyclopedias>
</Library>

Я знаю, что XSLT идеально подходит для этой работы, но это не вариант. Мы должны полностью выполнить это в SQL Server 2005. Любые реализации, не использующие XQuery, тоже подойдут, если это можно сделать полностью в T-SQL.

Ответы [ 2 ]

4 голосов
/ 18 сентября 2008

Из вашего примера мне не ясно, чего вы на самом деле пытаетесь достичь. Вы хотите вернуть новый XML со всеми удаленными узлами, кроме тех, которые удовлетворяют условию? Если да, то это похоже на работу для преобразования XSLT, которое, я не думаю, встроено в MSSQL 2005 (можно добавить как UDF: http://www.topxml.com/rbnews/SQLXML/re-23872_Performing-XSLT-Transforms-on-XML-Data-Stored-in-SQL-Server-2005.aspx).

Если вам просто нужно вернуть список узлов, то вы можете использовать это выражение:

//Book[not(@ID) or @ID = 5]

но у меня складывается впечатление, что это не то, что вам нужно. Было бы полезно, если бы вы могли привести более четкий пример.

Редактировать : Этот пример действительно более понятен. Лучшее, что я смог найти, это:

SET @Xml.modify('delete(//*[@category!=1])')
SELECT @Xml

Идея состоит в том, чтобы удалить из XML все узлы, которые вам не нужны, чтобы вы остались с исходной структурой и необходимыми узлами. Я проверил ваши два примера, и это дало желаемый результат.

Однако изменить имеет некоторые ограничения - кажется, вы не можете использовать его в операторе выбора, он должен модифицировать данные на месте. Если вам нужно вернуть такие данные с помощью выбора, вы можете использовать временную таблицу для копирования исходных данных, а затем обновить эту таблицу. Примерно так:

INSERT INTO #temp VALUES(@Xml)
UPDATE #temp SET data.modify('delete(//*[@category!=2])')

Надеюсь, это поможет.

1 голос
/ 18 сентября 2008

Вопрос не совсем понятен, но это то, что вы ищете?

DECLARE @Xml AS XML
        SET @Xml =
        N'
        <Books>
                <Book ID="1">Book1</Book>
                <Book ID="2">Book2</Book>
                <Book ID="3">Book3</Book>
                <Book>Book4</Book>
                <Book ID="5">Book5</Book>
                <Book ID="6">Book6</Book>
                <Book>Book7</Book>
                <Book ID="8">Book8</Book>
        </Books>
        '
DECLARE @BookID AS INT
SET @BookID = 5
DECLARE @Result AS XML

SET @result = (SELECT @xml.query('//Book[not(@ID) or @ID = sql:variable("@BookID")]'))
SELECT @result
...