Извлечение данных из XML в T SQL (Sql Server 2016) - PullRequest
1 голос
/ 04 марта 2020

Я пытаюсь найти способ избавиться от структур XML и получить оттуда все переменные и связанные с ними значения. Я хочу в конечном итоге разместить такие элементы в реальной (не временной) таблице. У меня есть сохраненный pro c, а также функция для этого, но она не работает. Я действительно не уверен, в чем причина и как заставить регулярное выражение извлекать только имена переменных и значения в строке, которые им принадлежат. Ребята, вы видите, что не так? Я посмотрел это в стеке, и все же я все еще застрял. Необработанная строка XML, из которой мне нужно извлечь данные: пока не работает так:

    CREATE FUNCTION dbo.RepetitiveReplace
    (
     @P_String VARCHAR(MAX),
     @P_Pattern VARCHAR(MAX),
     @P_ReplaceString VARCHAR(MAX),
     @P_ReplaceLength INT = 1
     )
         RETURNS VARCHAR(MAX)
         BEGIN
              DECLARE @Index INT;

              -- Get starting point of pattern
              SET @Index = PATINDEX(@P_Pattern, @P_String);

              while @Index > 0
              begin
                       --replace matching charactger at index
                       SET @P_String = STUFF(@P_String, PATINDEX(@P_Pattern, @P_String), @P_ReplaceLength, @P_ReplaceString);
                       SET @Index = PATINDEX(@P_Pattern, @P_String);
              end

              RETURN @P_String;
    END;

Ответы [ 3 ]

0 голосов
/ 04 марта 2020

Из вашего вопроса я узнаю, что XML, который вы предоставляете, уже «очищен» в некотором роде ... Было бы полезно, если бы вы привели пример своего реального XML (уменьшенного ...). Я почти уверен, что ничего из вышеперечисленного не требуется на самом деле.

При условии XML я бы использовал следующий запрос:

DECLARE @XML XML = 
'<var name="ENDA6012E0891_1">
  <string>N</string>
</var>
<var name="SAFETYRAILSTEPS_1">
  <string>Y</string>
</var>
<var name="HOMEVACANT_1">
  <string>N</string>
</var>';

- Запрос

select varElement.value('@name','varchar(100)') AS VarName
      ,varElement.value('(string/text())[1]','varchar(100)') AS VarValue
FROM @xml.nodes('/var') A(varElement);

Идея вкратце:

  • Мы используем .nodes() для извлечения каждого <var> элемента как производного набора.
  • Мы используем .value() для чтения содержимое атрибутов, которое возвращает имя переменной.
  • Мы снова используем .value(), но применяем релятивный XPath (без начального /) ниже текущего элемента <var>.

Если значения могут использовать разные типы (например, <int> вместо <string>), вы можете использовать

      ,varElement.value('(*/text())[1]','varchar(100)') AS VarValue

. Это будет читать содержимое первого элемента ниже <var>.

0 голосов
/ 07 марта 2020

Решение следующим образом. И да, эти очень старые данные отформатированы в записях как WDDX, но я просто избавлялся от значений WDDX, чтобы справиться с этим проще ... Я думал, что смогу просто ссылаться на переменные без этого ..

    SELECT  x.v.value('@name[1]', 'VARCHAR(100)') AS VarName,
    x.v.value('local-name((./*)[1])', 'VARCHAR(255)') as VarType,
    x.v.value('.','VARCHAR(255)') AS Value
    FROM @TempDataConv
    CROSS APPLY DecInfo.nodes('/wddxPacket/data/struct/var') AS x(v)
0 голосов
/ 04 марта 2020

Я собираюсь предположить, что под «переменными» вы ссылаетесь на XML атрибуты и что вы пытаетесь получить имя и значение атрибута. Используя ваши образцы данных, вы можете сделать это следующим образом:

DECLARE @XML XML = '
<var name="ENDA6012E0891_1"><string>N</string></var><var name="SAFETYRAILSTEPS_1"><string>Y</string></var><var name="HOMEVACANT_1"><string>N</string></var>';

SELECT 
  AttribName  = a.Attrib.value('local-name(.)','varchar(8000)'),
  AttribValue = a.Attrib.value('.','varchar(8000)')
FROM   @XML.nodes('//@*') AS a(Attrib);

Возвращает:

AttribName  AttribValue
----------- -------------------
name        ENDA6012E0891_1
name        SAFETYRAILSTEPS_1
name        HOMEVACANT_1

Для таблицы вы можете сделать это:

-- Sample Data
DECLARE @t TABLE (SomeId INT IDENTITY, SomeXML XML);
INSERT @t(SomeXML)
VALUES('<var name="ENDA6012E0891_1"></var><var name="SAFETYRAILSTEPS_1"></var><var name="HOMEVACANT_1"></var>'),
('<var dog="Blood Hound"></var><var cat="Orange One"></var><var creature="Dragon"></var><xxx color="pink"/>');

-- Solution
SELECT
  SomeId      = t.SomeId,
  AttribName  = a.Attrib.value('local-name(.)','varchar(8000)'),
  AttribValue = a.Attrib.value('.','varchar(8000)')
FROM        @t AS t
CROSS APPLY t.SomeXML.nodes('//@*') AS a(Attrib);

Возвращает:

SomeId      AttribName  AttribValue
----------- ----------- -------------------
1           name        ENDA6012E0891_1
1           name        SAFETYRAILSTEPS_1
1           name        HOMEVACANT_1
2           dog         Blood Hound
2           cat         Orange One
2           creature    Dragon
2           color       pink

С другой стороны: ваша функция repetitiveReplace (или Regex) не является инструментом для такого рода - используя SQL Собственный сервер * Поддержка 1044 * - XML - самое простое и эффективное решение. Тем не менее, я недавно создал аналогичную функцию, которая делает то же самое, но немного более мощный. Я написал это как T- SQL хвостовая рекурсия Функция высшего порядка Обычно я не фанат скалярных UDF, но этот парень прекрасно работает как скалярная встроенная функция, если у вас SQL Server 2019. Обратите внимание, что это всего 3 строки кода.

PowerStuff32:

CREATE FUNCTION dbo.PowerStuff32
(
  @String VARCHAR(8000),
  @Seek   VARCHAR(100),
  @StLen  TINYINT,
  @StWith VARCHAR(10)
)
/****************************************************************************************
Written by Alan Burstein 20200304

Note: This is a beta version of this function. It's a recursive scalar UDF and will error
out on 32 iterations. Add a counter for protection.

Note this example:
  DECLARE @String VARCHAR(1000) = 
    'Call me at 555-2233, 555 3344 or 1.800.555.6678 about case number 00234. Thanks!';

  SELECT f1.Txt 
  FROM   (VALUES(dbo.PowerStuff32(@string,
           '%[0-9][0-9][0-9]_[0-9][0-9][0-9][0-9]%',8,'<REMOVED>'))) AS f1(Txt);
*****************************************************************************************/
RETURNS VARCHAR(8000) WITH RETURNS NULL ON NULL INPUT AS BEGIN RETURN 
(
  SELECT      CHOOSE(st.C, @String, dbo.PowerStuff32(st.Txt,@Seek,@StLen,@StWith))
  FROM        (VALUES(PATINDEX(@Seek,@String)))                           AS nxt(D)
  CROSS APPLY (VALUES(STUFF(@String,nxt.D,@StLen,@StWith),SIGN(nxt.D)+1)) AS st(Txt,C)
);
END;

Пример (заменить номера телефонов текстом «Удалено»):

DECLARE @String VARCHAR(1000) = 
  'Call me at 555-2233, 555 3344 or 1.800.555.6678 about case number 00234. Thanks!';

SELECT f1.Txt 
FROM   (VALUES(dbo.PowerStuff32(@string,
         '%[0-9][0-9][0-9]_[0-9][0-9][0-9][0-9]%',8,'{REMOVED}'))) AS f1(Txt);

Возвращает:

Позвоните мне по адресу {УДАЛЕНО}, {УДАЛЕНО} или 1800. {УДАЛЕНО}, номер дела 00234 Спасибо!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...