OPENXML, Xsi: ноль и десятичные дроби - PullRequest
1 голос
/ 09 июня 2011

У меня есть XML, содержащий xsi: nil = "true" для определенных строковых и числовых элементов. Вот пример:

declare @data xml

set @data = '<?xml version="1.0" encoding="utf-8"?>
             <collection xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
              <item>
               <stringprop1>foo</stringprop1>
               <stringprop2 xsi:nil="true" />
               <decimalprop3 xsi:nil="true" />
              </item>
             </collection>'

Я хочу запросить этот XML в SQL Server 2008 R2. Я использую OPENXML, но он не очень хорошо работает с десятичными типами. Вот код, который я хотел бы написать:

declare @doc int
exec sp_xml_preparedocument @doc OUTPUT, @data;

select 
  stringprop1,
  stringprop2,
  decimalprop3  
from openxml(@doc, '/collection/item', 2)
with
(
  stringprop1 nvarchar(50)
  ,stringprop2 nvarchar(50)
  ,decimalprop3 decimal(18, 5)
)

exec sp_xml_removedocument @doc;

Это жалуется на преобразование nvarchar в десятичное число. После некоторого взлома я пришел к этому:

exec sp_xml_preparedocument @doc OUTPUT, @data;

select 
  nullif(stringprop1, '') as stringprop1,
  nullif(stringprop2, '') as stringprop2,
  convert(decimal(18, 5), nullif(decimalprop3, '')) as decimalprop3
from openxml(@doc, '/collection/item', 2)
with
(
  stringprop1 nvarchar(50)
  ,stringprop2 nvarchar(50)
  ,decimalprop3 nvarchar(50)
)

exec sp_xml_removedocument @doc;

Что хорошо, я думаю. Но есть ли способ сказать OPENXML, что xsi: nil означает NULL и что это нормально для десятичных чисел и строк?

Ответы [ 3 ]

2 голосов
/ 09 июня 2011

xsi: nil - это функция XML-схемы, и OpenXML был разработан до ее появления и не поддерживает xsi: nil. Поскольку вы используете SQL Server 2008, один из способов заставить это работать:

  1. ограничить XML соответствующей XML-схемой, которая проверяет данные и распознает xsi: nil и сопоставляет его с пустым значением.

  2. для извлечения данных используйте методы node () и value ().

С наилучшими пожеланиями Michael

1 голос
/ 08 мая 2012

Это должно сделать:

Объявить @doc intexec sp_xml_preparedocument @doc OUTPUT, @data, ''

select  
    stringprop1, 
    stringprop2, 
    decimalprop3 
from openxml(@doc, '/collection/item', 2) 
with 
( 
    stringprop1 nvarchar(50), 
    stringprop2 nvarchar(50) 'stringprop2[not(@xsi:nil = "true")]', 
    decimalprop3 nvarchar(50) 'decimalprop3[not(@xsi:nil = "true")]' 
) 

exec sp_xml_removedocument @doc; 
0 голосов
/ 17 июня 2019

Necromancing.Вот что у меня работает:

-- How to create the XML 
/*
DECLARE @xml XML 
SET @xml = ( SELECT (SELECT * FROM T_Benutzer FOR XML PATH('row'), ROOT('table'),  ELEMENTS xsinil) AS outerXml )

-- SELECT @xml 
*/


DECLARE @xml xml 
SET @xml = '<table xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <row>
    <PLK_UID>7CA68E6E-E998-FF92-BE70-126064765EAB</PLK_UID>
    <PLK_Status>1</PLK_Status>
    <BLA_UID>00000000-0000-0000-0000-000000000000</BLA_UID>
  </row>
  <row>
    <PLK_UID>C8A19BB1-6B45-67A6-0417-1F455EE8D2E1</PLK_UID>
    <PLK_Status>1</PLK_Status>
    <BLA_UID xsi:nil="true" />
  </row>
  <row>
    <PLK_UID>C8A19BB1-6B45-67A6-0417-1F455EE8D2E2</PLK_UID>
    <PLK_Status xsi:nil="true" />
    <BLA_UID xsi:nil="true" />
  </row>
  <row>
    <PLK_UID>C8A19BB1-0000-67A6-0417-1F455EE8D2E3</PLK_UID>
    <PLK_Status xsi:nil="true" />
    <BLA_UID>C8A19BB1-1111-67A6-0417-1F455EE8D2E1</BLA_UID>
  </row>
  <row>
    <PLK_UID>C8A19BB1-0001-67A6-0417-1F455EE8D2E4</PLK_UID>
    <PLK_Status>666</PLK_Status>
    <BLA_UID>C8A19BB1-1112-67A6-0417-1F455EE8D2E1</BLA_UID>
  </row>
</table>'


DECLARE @handle INT  
DECLARE @PrepareXmlStatus INT  

EXEC @PrepareXmlStatus = sp_xml_preparedocument @handle OUTPUT, @XML

SELECT 
     PLK_UID 
    ,PLK_Status 
    ,BLA_UID
FROM OPENXML(@handle, '/table/row', 2) WITH 
(
     PLK_UID uniqueidentifier 'PLK_UID[not(@*[local-name()="nil" and . ="true"])]' 
    ,PLK_Status int  'PLK_Status[not(@*[local-name()="nil" and . ="true"])]' 
    ,BLA_UID uniqueidentifier  'BLA_UID[not(@*[local-name()="nil" and . ="true"])]' 
)  

WHERE (1=1) -- AND

EXEC sp_xml_removedocument @handle 

Как создать список полей:

SELECT 
      CASE WHEN ORDINAL_POSITION = 1 THEN N'     ' ELSE N'    ,' END 
    + N'"' + COLUMN_NAME 
    + N'" '  
    + 
    CASE 
        WHEN DATA_TYPE = 'nvarchar' THEN N'national character varying' 
        WHEN DATA_TYPE = 'varchar' THEN N'character varying' 
        ELSE DATA_TYPE 
    END 
    + 
    CASE 
        WHEN DATA_TYPE IN ('char', 'nchar', 'binary') THEN 
                  N'(' 
                + CAST(CHARACTER_MAXIMUM_LENGTH AS nvarchar(36)) 
                + N')' 
        WHEN DATA_TYPE IN ('varchar', 'nvarchar', 'varbinary') THEN 
                N'(' + 
                CASE WHEN CHARACTER_MAXIMUM_LENGTH = -1 THEN 'MAX' ELSE CAST(CHARACTER_MAXIMUM_LENGTH AS nvarchar(36)) END 
                + N')' 
        WHEN DATA_TYPE IN ('datetimeoffset', 'datetime2', 'time', 'smalldatetime') THEN 
                  N'(' 
                + CAST(DATETIME_PRECISION AS nvarchar(36)) 
                + N')' 
        WHEN DATA_TYPE IN ('decimal', 'numeric') THEN 
                  N'(' 
                + CAST(NUMERIC_PRECISION AS nvarchar(36)) 
                + N',' 
                + CAST(NUMERIC_SCALE AS nvarchar(36)) 
                + N')' 

        ELSE N'' -- N'(default)' 
    END 
    + ' ''' + COLUMN_NAME + '[not(@*[local-name()="nil" and . ="true"])]''' 
FROM INFORMATION_SCHEMA.COLUMNS 
WHERE TABLE_SCHEMA = 'dbo' 
AND TABLE_NAME = 'T_Benutzer' 
ORDER BY ORDINAL_POSITION 
...