2010-04-29 87 views
4

我有一個存儲過程,它接受一個XML參數並將「實體」節點作爲記錄插入表中。這可以正常工作,除非其中一個數字字段的值爲XML中的空字符串。然後它拋出一個「錯誤轉換數據類型nvarchar數值」錯誤。帶有XML參數的SQL Server插入 - 數字爲空的字符串不轉換爲空

有沒有一種方法可以讓我告訴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

,如果您使用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作爲十進制值代替。

我不知道是否有任何方式與OPENXML方法做同樣的事情......

+0

我在另一個線程通知,性能會更糟糕......但是我如果我找不到其他解決方案,可能需要嘗試。 – Mayo 2010-04-29 16:05:55

+0

性能對成千上萬的記錄不利,但它給了我一個想法,我真的很感謝你的幫助,因爲我不會在沒有看到你的代碼的情況下得到這個想法。另外,我認爲你的解決方案適用於較小的數據集(和/或更快的服務器)。 – Mayo 2010-04-29 16:30:48

1

注:我就不用試了這一點,但marc_s把我在正確的軌道上,所以我會給他貸款爲答案。

我嘗試了Marc所建議的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 
+1

如果這是正確的答案,那麼標記爲這樣。我明白希望給予marc_s學分的幫助,但這會讓人們更難以看到有效的工作。另外,我不認爲marc正在出汗的點......;) – simon 2013-10-30 21:18:27

1

另一種解決方案是在接受一個字符串值,目標表中創建二次場。在用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語句以將相關VARCHAR列爲空時將目標INT字段轉換爲NULL。

這種方法可能提供比XQuery更好的性能。我會把這個留給這個線程中的其他人。

3

一種方式做到這一點是使用了/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 
+0

我認爲這是最好的解決方案,因爲節點是文本反正,你是騙取sql服務器給你一個null,如果不是null,那麼它的工作原理因爲它會正常工作。 – 2016-10-05 01:10:26

相關問題