Вставка SQL Server с параметром XML - пустая строка не преобразуется в ноль для числового значения - PullRequest
4 голосов
/ 29 апреля 2010

У меня есть хранимая процедура, которая принимает параметр XML и вставляет узлы «Entity» в виде записей в таблицу. Это работает нормально, если одно из числовых полей не имеет значение пустой строки в XML. Затем он выдает ошибку «Ошибка преобразования типа данных nvarchar в числовой».

Можно ли мне указать SQL преобразовать пустую строку в ноль для этих числовых полей в приведенном ниже коде?

-- @importData XML <- stored procedure param
DECLARE @l_index INT

EXECUTE sp_xml_preparedocument @l_index OUTPUT, @importData
INSERT INTO dbo.myTable
(
     [field1]
    ,[field2]
    ,[field3]
)
SELECT
     [field1]
    ,[field2]
    ,[field3]
FROM OPENXML(@l_index, 'Entities/Entity', 1)
    WITH 
    (
         field1 int 'field1'
        ,field2 varchar(40) 'field2'
        ,field3 decimal(15, 2) 'field3'
    )
EXECUTE sp_xml_removedocument @l_index

РЕДАКТИРОВАТЬ: И если это поможет, образец XML. Выдается ошибка, если я не закомментирую field3 в приведенном выше коде или не предоставлю значение в field3 ниже.

<?xml version="1.0" encoding="utf-16"?>
<Entities>
  <Entity>
    <field1>2435</field1>
    <field2>843257-3242</field2>
    <field3 />
  </Entity>
</Entities>

Ответы [ 4 ]

4 голосов
/ 29 апреля 2010

Если вы используете SQL Server XQuery, вы можете сделать что-то вроде этого:

SELECT
   nodes.entity.value('(field1)[1]', 'int') 'Field1',
   nodes.entity.value('(field2)[1]', 'varchar(50)') 'Field 2',
   CAST(ISNULL(nodes.entity.value('(field3)[1]', 'varchar(50)'), '0.00') 
        AS DECIMAL(15,2)) 'Field 3'
FROM
   @importData.nodes('/Entities/Entity') AS nodes(entity)

Как правило, преобразуйте значение field3 в строку, если оно равно NULL, вместо этого используйте 0,00 в качестве значения DECIMAL.

Я не знаю, есть ли способ с OPENXML сделать что-то подобное ...

3 голосов
/ 11 августа 2014

Один из способов сделать это - использовать функцию /text():

DECLARE @importData XML;
DECLARE @myTable TABLE
(
  field1 INT,
  field2 VARCHAR(40),
  field3 DECIMAL(15,2)
);

SET @importData =
'<?xml version="1.0"?>
<Entities>
  <Entity>
  <field1>2435</field1>
  <field2>843257-3242</field2>
  <field3 />
  </Entity>
</Entities>
';

INSERT INTO @myTable
(
  [field1],
  [field2],
  [field3]
)
SELECT
  t.tmp.value('(./field1)[1]', 'INT') AS field1,
  t.tmp.value('(./field2)[1]', 'VARCHAR(40)') AS field2,
  t.tmp.value('(./field3/text())[1]', 'DECIMAL(15,2)') AS field3
FROM
  @importData.nodes('//Entity') AS t(tmp);

SELECT * FROM @myTable;

Результатом будет:

field1 | field2      | field3
-----------------------------
2435   | 843257-3242 | NULL
1 голос
/ 13 октября 2011

Альтернативным решением будет создание дополнительного поля в вашей целевой таблице, которое принимает строковое значение. После заполнения таблицы данными XML выполните инструкцию, чтобы обновить целочисленное поле, установив его в NULL, если строковое поле пустое.

Например, вот моя строка XML.

'<xml>
    <d MetricID="1" Days="15" Sort="1" />
    <d MetricID="2" Days="45" Sort="2" />
    <d MetricID="3" Days="29" Sort="3" />
    <d MetricID="4" Days="119" Sort="4" />
    <d MetricID="5" Days="59" Sort="5" />
    <d MetricID="6" Days="179" Sort="6" />
    <d MetricID="7" Days="0" Sort="7" />
    <d MetricID="8" Days="" Sort="8" />
    <d MetricID="9" Days="60" Sort="9" />
</xml>'

Узлы 7 и 8 имеют соответствующие значения Дней 0 и Пусто. Ноль должен быть сохранен, а пробел должен быть сохранен как NULL. Вот мое решение для достижения этой цели.

DECLARE @idoc INT

EXEC sp_xml_preparedocument @idoc OUTPUT, @xmlDeadline
CREATE TABLE #Deadline
(       TimeframeMetricID INT
        , DeadlineDays INT NULL
        , DaysString VARCHAR(5) -- Hack for identifying DeadlineDays that were blank (null).
        , SortOrder INT
)

INSERT INTO #Deadline
        (TimeframeMetricID
       , DeadlineDays
       , DaysString
       , SortOrder
        )
SELECT  MetricID
        , Days
        , DaysString
        , Sort
FROM    OPENXML
(       @iDoc           -- Handle to the XML doc.
        , 'xml/d'       -- Map to the node that contains the attributes.
        , 1             --  Attribute centric mapping (1), instead of Element centric mapping (2).
)
WITH
(       MetricID INT '@MetricID'    -- Select attribute.  When selecting element remove @ sign.
        , Days  INT '@Days'
        , DaysString VARCHAR(5) '@Days' -- Hack for identifying DeadlineDays that were blank (null).
        , Sort  INT '@Sort'

) a

-- Release the XML parser
EXEC sp_xml_removedocument @idoc

-- Now convert Days that arrived as blank (i.e. Days="") from zero to NULL
-- to accurately reflect the supplied values.
UPDATE  #Deadline
SET     DeadlineDays = NULL
WHERE   (DaysString = '')

SELECT * FROM #Deadline

Чтобы вспомнить, что происходит выше. Код захватывает атрибут Days дважды. Поместите один в столбец INT и один в столбец VARCHAR. После выполнения оператора XML INSERT запустите инструкцию UPDATE, чтобы преобразовать целевое поле INT в NULL, когда соответствующий столбец VARCHAR пуст.

Этот подход может предлагать лучшую производительность, чем XQuery. Я оставлю это кому-то еще в этой теме.

1 голос
/ 29 апреля 2010

ПРИМЕЧАНИЕ. Я бы не стал пробовать это, но marc_s поставил меня на правильный путь, поэтому я дам ему кредит на ответ.

Я попробовал XQuery в соответствии с предложением Марка, но производительность была ужасна из-за количества записей, с которыми я имел дело. Но я использовал похожую технику в своем исходном коде ...

EXECUTE sp_xml_preparedocument @l_index OUTPUT, @importData
INSERT INTO dbo.myTable
(
     [field1]
    ,[field2]
    ,CASE
       WHEN [field3] = '' THEN NULL
       ELSE CAST([wl_llr] AS DECIMAL(15,2)) -- CHANGED TO CASE
     END
)
SELECT
     [field1]
    ,[field2]
    ,[field3]
FROM OPENXML(@l_index, 'Entities/Entity', 1)
    WITH 
    (
         field1 int 'field1'
        ,field2 varchar(40) 'field2'
        ,field3 varchar(50) 'field3' -- CHANGED TO VARCHAR
    )
EXECUTE sp_xml_removedocument @l_index
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...