Использование XQuery в SQL Server для возврата дубликатов - PullRequest
3 голосов
/ 15 мая 2019

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

Я использую XQuery для анализа XML-документа в наборе результатов SQL-сервера. Из-за некоторых повторяющихся пар имя / значение узла, я получаю дубликаты. Почему я получаю эти дубликаты?

Любая помощь или помощь с благодарностью.

XML:

<root>
  <record>
    <recordid>1</recordid>
    <tests>
      <test name="Food" value="Apple" />
      <test name="Drink" value="Water" />
    </tests>
  </record>
  <record>
    <recordid>2</recordid>
    <tests>
      <test name="Food" value="Banana" />
      <test name="Drink" value="Orange Juice" />
    </tests>
    <tests>
      <test name="Food" value="Steak" />
      <test name="Drink" value="Beer" />
    </tests>
  </record>
</root>

SQL:

DECLARE
    @XML XML = '<root><record><recordid>1</recordid><tests><test name="Food" value="Apple" /><test name="Drink" value="Water" /></tests></record><record><recordid>2</recordid><tests><test name="Food" value="Banana" /><test name="Drink" value="Orange Juice" /></tests><tests><test name="Food" value="Steak" /><test name="Drink" value="Beer" /></tests></record></root>'

DECLARE @XMLTable AS TABLE (
    MyXML XML
)   
INSERT INTO @XMLTable(MyXML)
VALUES(@XML)

SELECT
    rec.c.value('recordid[1]', 'INT') AS RecordId,
    t.c.value('test[@name="Food"][1]/@value[1]', 'VARCHAR(15)') AS Food,
    t.c.value('test[@name="Drink"][1]/@value[1]', 'VARCHAR(15)') AS Drink
FROM @XMLTable AS x
CROSS APPLY MyXML.nodes('/root/record') rec(c)
OUTER APPLY MyXML.nodes('/root/record/tests') t(c)

Результаты:

RecordId    Food            Drink
----------- --------------- ---------------
1           Apple           Water
1           Banana          Orange Juice
1           Steak           Beer
2           Apple           Water
2           Banana          Orange Juice
2           Steak           Beer

Ожидаемые результаты:

RecordId    Food            Drink
----------- --------------- ---------------
1           Apple           Water
2           Banana          Orange Juice
2           Steak           Beer

Ответы [ 3 ]

3 голосов
/ 15 мая 2019

Примерно так будет:

SELECT
    rec.c.value('../recordid[1]', 'INT') AS RecordId,
    rec.c.value('test[@name="Food"][1]/@value[1]', 'VARCHAR(15)') AS Food,
    rec.c.value('test[@name="Drink"][1]/@value[1]', 'VARCHAR(15)') AS Drink
FROM @XMLTable 
CROSS APPLY MyXML.nodes('/root/record/tests') rec(c)
2 голосов
/ 16 мая 2019

Лучший способ решить эту проблему, на самом деле, был следующим:

SELECT
    rec.value('recordid[1]', 'INT') AS RecordId,
    t.value('test[@name="Food"][1]/@value[1]', 'VARCHAR(15)') AS Food,
    t.value('test[@name="Drink"][1]/@value[1]', 'VARCHAR(15)') AS Drink
FROM @XMLTable AS x
CROSS APPLY MyXML.nodes('/root/record') A(rec)
OUTER APPLY rec.nodes('tests') b(t);

Разница с вашим собственным решением: второе APPLY использует вывод первого APPLY для более глубокого погружения ввозвращенный фрагмент XML.

Вы можете прочитать его как:

  • Погрузиться в XML и вернуть каждый <record> в одну строку.
  • Теперь (подумайте по ряду ) возьмите возвращенный <record> и откройте <tests>.Это относительный путь, следовательно, без косых черт.
  • OUTER допускает пустое <tests>

И общий совет: обратная навигация (A Xpath с ../) - известный убийца производительности.Существует отличный ответ Микаэля Эрикссона, объясняющего фон

0 голосов
/ 15 мая 2019

Мне удалось достичь ожидаемых результатов, добавив предложение WHERE к моему исходному сценарию.

SQL:

SELECT
    rec.c.value('recordid[1]', 'INT') AS RecordId,
    t.c.value('test[@name="Food"][1]/@value[1]', 'VARCHAR(15)') AS Food,
    t.c.value('test[@name="Drink"][1]/@value[1]', 'VARCHAR(15)') AS Drink
FROM @XMLTable AS x
CROSS APPLY MyXML.nodes('/root/record') rec(c)
CROSS APPLY MyXML.nodes('/root/record/tests') t(c)
WHERE rec.c.value('recordid[1]', 'INT') = t.c.value('../recordid[1]', 'INT')

Результаты:

RecordId    Food            Drink
----------- --------------- ---------------
1           Apple           Water
2           Banana          Orange Juice
2           Steak           Beer
...