Как использовать параметр в XQuery? - PullRequest
0 голосов
/ 11 июня 2019

Я пытаюсь зациклить значение XML с помощью переменной @count, но не могу понять, как использовать параметр

create function sbill(@s xml) 
returns varchar(max)
as begin

DECLARE @SBILLNO VARCHAR(MAX),@count int
set @count =5
while @count <> 0
Begin
   select @SBILLNO= @s.value('(/Transaction/BodyData/TransBody/BillwiseData/BillAdjustment/sBillNo)[@count]','varchar(max)')
   set @count=@count-1
end
returns @SBILLNO
end

Ответы [ 3 ]

1 голос
/ 12 июня 2019

Ваш собственный код выглядит так, как будто вы хотите получить несколько повторяющихся элементов из XML.Поэтому вы используете цикл WHILE для чтения первого, второго, n-го значения одно за другим.Правильно ли пока?

Предположительно, вы действительно ищете это:

--create a mockup-table with two rows
DECLARE @tbl TABLE(ID INT IDENTITY, SomeXml XML);
INSERT INTO @tbl VALUES
 ('<a>
      <b>b11</b>
      <b>b12</b>
      <b>b13</b>
      <b>b14</b>
      <b>b15</b>
   </a>>')
,('<a>
      <b>b21</b>
      <b>b22</b>
      <b>b23</b>
    </a>');

- Запрос

SELECT b.value('text()[1]','varchar(10)')
FROM @tbl t
CROSS APPLY t.SomeXml.nodes('/a/b') A(b);

Метод .nodes() будет возвращать каждый <b> в одной строке как производная таблица (имя таблицы A, имя столбца b. Вы можете установить оба имени так, как вам нравится. Возвращаемый столбец имеет тип XML, и онявляется относительным фрагментом. В этом столбце для извлечения содержимого используется метод .value().

Некоторые подсказки, как избежать циклов

В любом случае следует избегать циклов всякий раз, когдаВы можете избежать их. Ваш собственный подход может быть решен на основе набора способом с подсчетом на лету :

DECLARE @count INT=5;

WITH Tally(Nmbr) AS (SELECT TOP(@count) ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) FROM master..spt_values)
SELECT t.ID
      ,Nmbr
      ,t.SomeXml.value('(/a/b[sql:column("Nmbr")])[1]','varchar(10)') AS Numbered_B
FROM @tbl t
CROSS JOIN Tally;

Это будетиспользуйте список от 1 до 5, чтобы прочитать элементы XML по их позиции, используя sql:column(). Вторая строка вернет два NULL-значения, так как нет четвертого или пятого элемента.

Мы могли бы дажеиспользуйте динамически создаваемый список номеров

Но вы даже можете создать Tally-on-the-fly в зависимости от фактического количества <b> элементов, как здесь:

SELECT t.ID
      ,Nmbr
      ,t.SomeXml.value('(/a/b[sql:column("Nmbr")])[1]','varchar(10)') AS Numbered_B
FROM @tbl t
CROSS APPLY (SELECT TOP(t.SomeXml.value('count(/a/b)','int')) ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) FROM master..spt_values) A(Nmbr);

Это работает как запрос раньше, но будет использовать список от 1 до n, где n найдено динамически.

0 голосов
/ 11 июня 2019

Несколько методов:
1. Использование sql:variable():

select @SBILLNO= 
@s.value('(/Transaction/BodyData/TransBody/BillwiseData/BillAdjustment/sBillNo)[sql:variable("@count")]','varchar(max)')
Для более сложных запросов вам может потребоваться построить динамический XQuery с использованием динамического SQL.
0 голосов
/ 11 июня 2019

Попробуйте sql:variable https://docs.microsoft.com/en-us/sql/xquery/xquery-extension-functions-sql-variable?view=sql-server-2017

Демо

declare @x xml =
'<a>
  <b>123</b>
  <b>456</b>
</a>'
declare @count int = 2;

select t.n.value('(/a/b)[sql:variable("@count")][1]','int')
from @x.nodes('.') t(n);
...