tsql:xml节点转换错误

时间:2012-10-15 17:26:50

标签: sql sql-server casting xml-parsing

我需要做什么才能容纳Xml doc上的传入值,这些值最终被写为十进制(18,5),int或唯一标识符,这些标识符可能在标记内没有数据[虽然不是NULL数据 - 因为我发现了困难的方式],或标签中的文本数据。似乎我解决了一个问题,但创建另一个问题!: - 我收到错误:转换varchar值时转换失败' 7800.00000'到数据类型int 从字符串转换为uniqueidentifier时转换失败。我该如何纠正?

以下是带有数据的xml代码段示例:

<Products>
  <Product>
      <AgreementId>2439</AgreementId>
      <Difference>0.00400</Difference>
      <DispatchedQuantity>7800.00000</DispatchedQuantity>
      <Freight>0.01560</Freight>
      <ProductGUID>d475165c-0031-41f6-ad44-cecb73dfd2de</ProductGUID>
      <ShortName>U_B5Not Specif_No2_U</ShortName>
    </Product>
  </Products

这是在存储过程中处理它的代码:

 SELECT
       l.OrderId,
       l.OrderLiftId,
       cast(n.x.value('AgreementId[1]', 'varchar(20)') as int),
       CAST(CASE WHEN n.x.value('Difference[1]', 'varchar(20)') = '' THEN 0 ELSE  COALESCE(n.x.value('Difference[1]', 'varchar(20)'), '0.00') END AS decimal(18,5)),
       CAST(CASE WHEN n.x.value('DispatchedQuantity[1]', 'varchar(20)') = '' THEN 0 ELSE  COALESCE(n.x.value('DispatchedQuantity[1]', 'varchar(20)'), '0.00') END AS decimal(18,5)),
       CAST(CASE WHEN n.x.value('Freight[1]', 'varchar(20)') = '' THEN 0 ELSE  COALESCE(n.x.value('Freight[1]', 'varchar(20)'), '0.00') END AS decimal(18,5)),
       cast(n.x.value('ProductGUID[1]', 'varchar(40)') as uniqueidentifier),
       n.x.value('ShortName[1]', 'varchar(100)')
  FROM @lines as l
   CROSS APPLY l.lineprods.nodes('/Products/Product') as n(x);

更新:此行 -

 CAST(CASE WHEN n.x.value('DispatchedQuantity[1]', 'varchar(20)') = '' THEN 0 ELSE  COALESCE(n.x.value('DispatchedQuantity[1]', 'varchar(20)'), '0.00') END AS decimal(18,5)),

需要更改为:

 CAST(CASE WHEN n.x.value('DispatchedQuantity[1]', 'varchar(20)') = '' THEN 0 ELSE  COALESCE(n.x.value('DispatchedQuantity[1]', 'decimal(18,5)'), '0.00') END AS decimal(18,5)),

以容纳非数字/非空值和第一个非空的,我希望是一个小数 - 如果我得到一些其他的hokey值,它仍然可能会爆炸。关于如何处理的任何建议?

2 个答案:

答案 0 :(得分:1)

试试这个。

declare @xml xml 

select @xml = '
<Products>
  <Product>
      <AgreementId>2439</AgreementId>
      <Difference>aaa</Difference>
      <DispatchedQuantity>7800.00000</DispatchedQuantity>
      <Freight>0.01560</Freight>
      <ProductGUID>d475165c-0031-41f6-ad44-cecb73dfd2de</ProductGUID>
      <ShortName>U_B5Not Specif_No2_U</ShortName>
    </Product>
  </Products>'

select
    CALC2.Agreement,
    CALC2.[Difference],
    CALC2.DispatchedQuantity,
    CALC2.ProductGUID,
    CALC2.Freight,
    CALC.ShortName
from @xml.nodes('/Products/Product') as n(x)
    outer apply
    (
        select
            isnull(n.x.value('AgreementId[1]', 'varchar(20)'), '0') as Agreement,
            isnull(n.x.value('Difference[1]', 'varchar(20)'), '0.00000') as     [Difference],
            isnull(n.x.value('DispatchedQuantity[1]', 'varchar(20)'), '0.00000') as DispatchedQuantity,
            n.x.value('ProductGUID[1]', 'varchar(40)') as ProductGUID,
            isnull(n.x.value('Freight[1]', 'varchar(20)'), '0.00000') as Freight,
            n.x.value('ShortName[1]', 'varchar(100)') as ShortName
    ) as CALC
    outer apply
    (
        select
            case when CALC.Agreement not like '%[^0-9]%' then cast(CALC.Agreement as int) else null end as Agreement,
            case when CALC.[Difference] not like '%[^0-9]%' then cast(CALC.[Difference] as decimal(18,5)) else null end as [Difference],
            case when CALC.DispatchedQuantity not like '%[^0-9]%' then null else cast(CALC.DispatchedQuantity as decimal(18,5)) end as DispatchedQuantity,
            cast(CALC.ProductGUID as uniqueidentifier) as ProductGUID,
            case when CALC.Freight not like '%[^0-9]%' then null else cast(CALC.Freight as decimal(18,5)) end as Freight
    ) as CALC2

答案 1 :(得分:1)

我认为ISNUMERIC函数可以解决问题。

CASE WHEN ISNUMERIC(n.x.value('DispatchedQuantity[1]', 'varchar(max)')) = 0 THEN 0 ELSE ... END

但是,我不明白为什么在将xml转换为十进制之前将xml提取为varchar(20)。一旦你建立了数据是数字,你可以简单地使用:

n.x.value('DispatchedQuantity[1]', ' decimal(18,5)')

我用于测试的完整SQL:

DECLARE @XML XML
SET @XML = '<Products>
              <Product>
                  <AgreementId>2439</AgreementId>
                  <Difference>0.00400</Difference>
                  <DispatchedQuantity>7800.00000</DispatchedQuantity>
                  <Freight>0.01560</Freight>
                  <ProductGUID>d475165c-0031-41f6-ad44-cecb73dfd2de</ProductGUID>
                  <ShortName>U_B5Not Specif_No2_U</ShortName>
                </Product>
                <Product>
                  <AgreementId>2439</AgreementId>
                  <Difference></Difference>
                  <DispatchedQuantity>INVALID NUMBER</DispatchedQuantity>
                  <Freight>INVALID FREIGHT</Freight>
                  <ProductGUID>INVALID GUID</ProductGUID>
                  <ShortName>U_B5Not Specif_No2_U</ShortName>
                </Product>
              </Products>'

SELECT  [AgreementId] = CAST(n.x.value('AgreementId[1]', 'varchar(20)') AS INT),
        [Difference] = CAST(CASE WHEN ISNUMERIC(n.x.value('Difference[1]', 'varchar(max)')) = 0 THEN 0 ELSE n.x.value('Difference[1]', 'decimal(18, 5)') END AS DECIMAL(18,5)),
        [DispatchedQuantity] = CAST(CASE WHEN ISNUMERIC(n.x.value('DispatchedQuantity[1]', 'varchar(max)')) = 0 THEN 0 ELSE n.x.value('DispatchedQuantity[1]', 'decimal(18, 5)') END AS DECIMAL(18,5)),
        [Freight] = CAST(CASE WHEN ISNUMERIC(n.x.value('Freight[1]', 'varchar(20)')) = 0 THEN 0 ELSE n.x.value('Freight[1]', ' decimal(18,5)') END AS DECIMAL(18,5)),
        [ProductGUID] = CASE WHEN ProductGUID LIKE Expression + '%' OR ProductGUID LIKE '{' + Expression + '}' THEN CAST(ProductGUID AS UNIQUEIDENTIFIER) END,
        [ShortName] = n.x.value('ShortName[1]', 'varchar(100)')
FROM    @XML.nodes('/Products/Product') AS n(x)
        CROSS APPLY 
        (   SELECT  [ProductGUID] = n.x.value('ProductGUID[1]', 'varchar(40)') ,
                    [Expression] = REPLACE('00000000-0000-0000-0000-000000000000', '0', '[0-9a-fA-F]') COLLATE Latin1_General_BIN 
        ) Expr 

修改

我已编辑上述查询以允许无效的GUID(在Martin Smith的帮助下)

如果您使用的是SQL-Server 2012,则可以使用TRY_CONVERT

SELECT  [AgreementId] = TRY_CONVERT(INT, n.x.value('AgreementId[1]', 'varchar(20)')),
        [Difference] =TRY_CONVERT(DECIMAL(18, 5), n.x.value('Difference[1]', 'varchar(max)')),
        [DispatchedQuantity] = TRY_CONVERT(DECIMAL(18, 5), n.x.value('DispatchedQuantity[1]', 'varchar(max)')),
        [Freight] = TRY_CONVERT(DECIMAL(18, 5), n.x.value('Freight[1]', 'varchar(20)')),
        [ProductGUID] = TRY_CONVERT(UNIQUEIDENTIFIER, n.x.value('ProductGUID[1]', 'varchar(40)')),
        [ShortName] = n.x.value('ShortName[1]', 'varchar(100)')
FROM    @XML.nodes('/Products/Product') AS n(x)