SQL XML Access узел 'n' в цикле - PullRequest
       8

SQL XML Access узел 'n' в цикле

0 голосов
/ 19 сентября 2018

У меня есть асинхронная система аудита, которую я написал для SQL Server, которая использует триггеры для отправки сообщения в очередь компонента Service Broker, который вызывает хранимую процедуру для обработки очереди и регистрации сведений.

Все этоотлично работает, если происходит одно изменение (обновление или удаление), используя следующие фрагменты кода: -

Получить сообщение из очереди:

DECLARE @Message XML;
RECEIVE TOP(1) @Message = CONVERT(NVARCHAR(MAX), message_body) FROM AuditLogReceiveQueue;

Затем я сохраняю имя поляЯ хочу извлечь в переменную @ fieldname

Получить нужный мне узел из XML

SET @InsertedValue = (SELECT @Message.value('(/Inserts/*[local-name()=sql:variable("@fieldname")])[1]', 'NVARCHAR(Max)'))

Все хорошо, это получает значение, которое я хочу.

Тем не менее, возможно наличие нескольких узлов Inserts, поэтому мне нужно пройти их все.

Доступ к определенному узлу Inserts достаточно прост, следующее работает при наличии трех (или более)

SET @InsertedValue = (SELECT @Message.value('(/Inserts/*[local-name()=sql:variable("@fieldname")])[1]', 'NVARCHAR(Max)'))
SET @InsertedValue = (SELECT @Message.value('(/Inserts/*[local-name()=sql:variable("@fieldname")])[2]', 'NVARCHAR(Max)'))
SET @InsertedValue = (SELECT @Message.value('(/Inserts/*[local-name()=sql:variable("@fieldname")])[3]', 'NVARCHAR(Max)'))

Немного трудно увидеть с узкими кодовыми полями, но если вы прокрутите вправо, вы увидите число в квадратных скобках рядом с концом, это номер узла, [1], [2], [3] и т. д.

Итак, по логике мне просто нужно иметь переменную, содержащую количество узлов Inserts, и пройти через нее: -

DECLARE @nEntries INT = (SELECT @Message.value('count(/Inserts)', 'int'))

SET @InsertedValue = (SELECT @Message.value('(/Inserts/*[local-name()=sql:variable("@fieldname")])[sql:variable("@nEntries")]', 'NVARCHAR(Max)'))

@ nEntries содержит правильное число, так что часть работает (при независимом тестировании)однако строка SET @InsertedValue = не позволяет даже запустить скрипт ALTER PROCEDURE, он падает с: -

XQuery [value()]: 'value()' requires a singleton (or empty sequence),
    found operand of type 'xdt:untypedAtomic *'

на SET @InsertedValue = строка

Я попытался привести переменную как целое число несколькими способами: -

[xs: integer (sql: variable ("@ nEntries"))]

SET @InsertedValue = (SELECT @Message.value('(/Inserts/*[local-name()=sql:variable("@fieldname")])[xs:integer(sql:variable("@nEntries"))]', 'NVARCHAR(Max)'))

Это дает то же самоеошибка: -

XQuery [value()]: 'value()' requires a singleton (or empty sequence),
    found operand of type 'xdt:untypedAtomic *'

[sql: переменная ("@ nEntries") приведена как xs: integer]

SET @InsertedValue = (SELECT @Message.value('(/Inserts/*[local-name()=sql:variable("@fieldname")])[sql:variable("@nEntries") cast as xs:integer]', 'NVARCHAR(Max)'))

Это дает:

XQuery [value()]: In this version of the server, 'cast as <type>' is not available. 
Please use the 'cast as <type> ?' syntax.

[sql: variable ("@ nEntries") приводится как xs: integer?]

SET @InsertedValue = (SELECT @Message.value('(/Inserts/*[local-name()=sql:variable("@fieldname")])[sql:variable("@nEntries") cast as xs:integer ?]', 'NVARCHAR(Max)'))

Это возвращает меня к

XQuery [value()]: 'value()' requires a singleton (or empty sequence),
    found operand of type 'xdt:untypedAtomic *'

[sql: variable ("@ nEntries")]приведено как xs: целое число?

SET @InsertedValue = (SELECT @Message.value('(/Inserts/*[local-name()=sql:variable("@fieldname")])[sql:variable("@nEntries")] cast as xs:integer ?', 'NVARCHAR(Max)'))

Это в основном мне принадлежит: -

XQuery [value()]: Cannot explicitly convert from 'xdt:untypedAtomic *' to 'xs:integer ?'

Согласно: -

https://docs.microsoft.com/en-us/sql/xquery/type-casting-rules-in-xquery?view=sql-server-2017

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

Итак, не уверен, почему он стонет об этом.

Я пробовал без квадратных скобок: sql: variable ("@ nEntries")

SET @InsertedValue = (SELECT @Message.value('(/Inserts/*[local-name()=sql:variable("@fieldname")])sql:variable("@nEntries")', 'NVARCHAR(Max)'))

Это дает мне

XQuery [value()]: No more tokens expected at the end of the XQuery expression. Found 'sql'.

Я пытался использовать дополнительные квадратные скобки: [[sql: variable ("@ nEntries")] приведен как xs: integer?]

SET @InsertedValue = (SELECT @Message.value('(/Inserts/*[local-name()=sql:variable("@fieldname")])[[sql:variable("@nEntries")] cast as xs:integer ?]', 'NVARCHAR(Max)'))

Это дает

XQuery [value()]: Syntax error near '['

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

SET @InsertedValue = (SELECT @Message.value('(/Inserts/*[local-name()=sql:variable("@fieldname")])[' + CONVERT(NVARCHAR(Max),@nEntries) + '] cast as xs:integer ?]', 'NVARCHAR(Max)'))

Как и ожидалось, это дало

The argument 1 of the XML data type method "value" must be a string literal.

Я также попытался использовать переменную: -

DECLARE @Query NVARCHAR(Max) = '(/Inserts/*[local-name()=sql:variable("@fieldname")])[' + CONVERT(NVARCHAR(Max),@nEntries) + '] cast as xs:integer ?]'
SET @InsertedValue = (SELECT @Message.value(@Query, 'NVARCHAR(Max)'))

Снова

The argument 1 of the XML data type method "value" must be a string literal.

Ивот почему я решил попросить помощи у вас, уважаемые ребята!

Учитывая, что я использую sql: variable довольно успешно в том же самом вызове (ниже работает нормально, но получает только первые данные Inserts)...

SET @InsertedValue = (SELECT @Message.value('(/Inserts/*[local-name()=sql:variable("@fieldname")])[1]', 'NVARCHAR(Max)'))

... у кого-нибудь есть идеи, которые я не пытался разрешить использовать переменную sql: для номера узла?

Edit:

Я также пытался [sql: variable ("@ nEntries") [1]], на случай, если бы он думал, что @nEntries может иметь несколько значений

SET @InsertedValue = (SELECT @Message.value('(/Inserts/*[local-name()=sql:variable("@fieldname")])[sql:variable("@nEntries")[1]]', 'NVARCHAR(Max)'))

Дает нашему старому другу

XQuery [value()]: 'value()' requires a singleton (or empty sequence), found operand of type 'xdt:untypedAtomic *'

Обходной путь Edit: -

Я создал обходной путь, где я создаю временную таблицу и вставляю по одной строке с каждой соответствующей записью Inserts and Deletes, а затем просматриваю эту таблицу.Это позволяет мне использовать [1], как и раньше, что прекрасно работает.

Но я все же хотел бы знать, как использовать переменную, как указано выше, для будущего использования!

...