Выбор нулевого значения из XML в SQL Server

Я пытаюсь выбрать из XML, который имеет null как один из атрибутов. Вместо того, чтобы возвращать null, он возвращает 0. Что я делаю неправильно?
См. Следующий код для тиражирования:

declare @a xml
select @a = '<TestSet xmlns:xsi="http://www.w3.org/2001/XMLSchema-instace">
  <Element>
    <Property1>1</Property1>
    <Property2>1</Property2>
  </Element>
  <Element>
    <Property1 xsi:nil="true" />
    <Property2>2</Property2>
  </Element>
  <Element>
    <Property1>3</Property1>
    <Property2>3</Property2>
  </Element>
</TestSet>'

 select ParamValues.TaskChainerTask.query('Property1').value('.','int') as Property1,
        ParamValues.TaskChainerTask.query('Property2').value('.','int') as Property2
   from @a.nodes('(/TestSet/Element)') as ParamValues(TaskChainerTask)

возвращает:

Property1   Property2
1           1
0           2
3           3

Это возвращает то же самое:

declare @a xml
select @a = '<TestSet xmlns:xsi="http://www.w3.org/2001/XMLSchema-instace">
  <Element>
    <Property1>1</Property1>
    <Property2>1</Property2>
  </Element>
  <Element>
    <Property1 xsi:nil="true" />
    <Property2>2</Property2>
  </Element>
  <Element>
    <Property1>3</Property1>
    <Property2>3</Property2>
  </Element>
</TestSet>'

 select ParamValues.TaskChainerTask.query('Property1').value('.','int') as Property1,
        ParamValues.TaskChainerTask.query('Property2').value('.','int') as Property2
   from @a.nodes('(/TestSet/Element)') as ParamValues(TaskChainerTask)

Спасибо заранее.

Ответ 1

Я думаю, что если вы используете функцию number(), вы получите нуль, как ожидалось. Это работает только для типов номеров:

select 
   ParamValues.TaskChainerTask.query('Property1').value('number(.)','int') as Property1,         
   ParamValues.TaskChainerTask.query('Property2').value('number(.)','int') as Property2
from @a.nodes('(/TestSet/Element)') as ParamValues(TaskChainerTask) 

Ответ 2

http://go4answers.webhost4life.com/Example/including-null-columns-empty-elements-125474.aspx

[not(@xsi:nil = "true")]

Это выберет значение null. Кстати, у автора кода есть опечатка

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instace"

экземпляр с ошибкой, как instace

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

Рабочая версия авторского кода

declare @a xml
            select @a = '<TestSet xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
              <Element>
                <Property1>1</Property1>
                <Property2>1</Property2>
              </Element>
              <Element>
                <Property1 xsi:nil="true" />
                <Property2>2</Property2>
              </Element>
              <Element>
                <Property1>3</Property1>
                <Property2>3</Property2>
              </Element>
            </TestSet>'

             select ParamValues.TaskChainerTask.value('./Property1[1][not(@xsi:nil = "true")]','int') as Property1,
                    ParamValues.TaskChainerTask.value('./Property2[1][not(@xsi:nil = "true")]','int') as Property2
               from @a.nodes('(/TestSet/Element)') as ParamValues(TaskChainerTask)

Ответ 3

Поскольку вы устанавливаете поля в INT, у вас возникает проблема, что оба поля xsi: nil = "true", а значение 0 будет равно 0 в качестве значения по умолчанию для INT Is 0.

Сначала вы можете преобразовать в VARCHAR, чтобы обнаружить пустую строку (''), которые строковые поля, содержащие xsi: nil = "true", производят, а затем преобразуют результат в INT.

Этот SELECT даст вам ответ после

SELECT  CONVERT(INT,NULLIF(ParamValues.TaskChainerTask.query('Property1').value('.', 'varchar(5)'),'')) AS Property1
      , CONVERT(INT,NULLIF(ParamValues.TaskChainerTask.query('Property2').value('.', 'varchar(5)'),'')) AS Property2
FROM    @a.nodes('(/TestSet/Element)') AS ParamValues (TaskChainerTask) 

В результате этого вы получите:

Property1   Property2
1           1
NULL        2
3           3

Ответ 4

Я просто использовал NULLIF для превращения пустых строк в NULL по мере необходимости.

Теперь вы можете сгенерировать nil с помощью FOR XML, но я никогда не разрабатывал, как разбирать его, извините...

Ответ 5

Я бы назвал этот подход:

DECLARE @a XML = '<TestSet xmlns:xsi="http://www.w3.org/2001/XMLSchema-instace">
  <Element>
    <Property1>1</Property1>
    <Property2>1</Property2>
  </Element>
  <Element>
    <Property1 xsi:nil="true" />
    <Property2>2</Property2>
  </Element>
  <Element>
    <Property1>3</Property1>
    <Property2>3</Property2>
  </Element>
</TestSet>'

SELECT
    ParamValues.TaskChainerTask
        .value('./Property1[not(./@*[local-name()="nil"] = "true")][1]', 'int') as Property1,
    ParamValues.TaskChainerTask
        .value('./Property2[not(./@*[local-name()="nil"] = "true")][1]', 'int') as Property2
FROM @a.nodes('//Element') ParamValues(TaskChainerTask)

Ответ 6

Умный способ сделать это - удалить Property1 node из XML, где требуются нулевые значения. Итак, что когда-либо node вы хотите быть пустым в своем результирующем наборе, просто не добавляйте его в XML. Таким образом, вам не нужно будет добавлять атрибут xsi: nill.

Итак, ниже также будет указано значение null:

declare @a xml
select @a = '<TestSet>
  <Element>
    <Property1>1</Property1>
    <Property2>1</Property2>
  </Element>
  <Element>
     //I have removed the property1 node and this will result in a null value
    <Property2>2</Property2>
  </Element>
  <Element>
    <Property1>3</Property1>
    <Property2>3</Property2>
  </Element>
</TestSet>'

 select ParamValues.TaskChainerTask.value('./Property1[1]','int') as Property1,
        ParamValues.TaskChainerTask.value('./Property2[1]','int') as Property2
   from @a.nodes('(/TestSet/Element)') as ParamValues(TaskChainerTask)

В приведенном выше примере видно, что свойства Property1 node не существует, и это приведет к нулевому значению

Ответ 7

Я не уверен, что ваш конкретный случай требует, чтобы вы сначала запрашивали узлы, но если вы не можете запросить значение и предоставить xPath. Поскольку Property1 node существует, вы хотите оценить текст() Property1 node, а не сам node:

 select  ParamValues.TaskChainerTask.value('Property1[1]/text()[1]','int') as Property1,
        ParamValues.TaskChainerTask.value('Property2[1]/text()[1]','int') as Property2

   from @a.nodes('(/TestSet/Element)') as ParamValues(TaskChainerTask)

Кроме того, чтобы убедиться, что это работает в других случаях, вы можете предоставить наиболее подробный путь к элементу в stat @a.nodes xPath и подойти с помощью "../", а не подчиняться результатам node.