如何从XML *

时间:2016-08-17 16:13:11

标签: sql sql-server xml xpath null

我有一些XML,我试图使用他们的XML datatype functions插入到Microsoft SQL Server数据库中。

其中一个表字段是可以为空的DATE列。如果节点丢失,则将其作为NULL插入,这很棒。但是,如果节点存在但在运行XPath查询时为空<LastDay/>,则它会将空节点中的值解释为empty string '' instead of NULL。因此,在查看表结果时,默认情况下会将日期转换为1900-01-01。

我希望空节点也可以插入NULL而不是默认的空字符串''或1900-01-01。如何让它插入NULL

CREATE TABLE myxml 
(
    "id" INT,
    "name" NVARCHAR(100),
    "company" NVARCHAR(100),
    "lastday" DATE
);

DECLARE @xml XML =
'<?xml version="1.0" encoding="UTF-8"?>
<Data xmlns="http://example.com" xmlns:dmd="http://example.com/data-metadata">
    <Company dmd:name="Adventure Works Ltd.">
        <Employee id="1">
            <Name>John Doe</Name>
            <LastDay>2016-08-01</LastDay>
        </Employee>
        <Employee id="2">
            <Name>Jane Doe</Name>
        </Employee>
    </Company>
    <Company dmd:name="StackUnderflow">
        <Employee id="3">
            <Name>Jeff Puckett</Name>
            <LastDay/>
        </Employee>
        <Employee id="4">
            <Name>Ill Gates</Name>
        </Employee>
    </Company>
</Data>';    

WITH XMLNAMESPACES (DEFAULT 'http://example.com', 'http://example.com/data-metadata' as dmd)
INSERT INTO myxml (id,name,company,lastday)
SELECT 
    t.c.value('@id',         'INT' ),
    t.c.value('Name[1]',     'VARCHAR(100)' ),
    t.c.value('../@dmd:name','VARCHAR(100)' ),
    t.c.value('LastDay[1]',  'DATE' )
FROM @xml.nodes('/Data/Company/Employee') t(c)

这会产生:

 id  name         company              lastday
 ------------------------------------------------
 1   John Doe     Adventure Works Ltd. 2016-08-01
 2   Jane Doe     Adventure Works Ltd. NULL
 3   Jeff Puckett StackUnderflow       1900-01-01
 4   Ill Gates    StackUnderflow       NULL

我正在努力实现:

 id  name         company              lastday
 ------------------------------------------------
 1   John Doe     Adventure Works Ltd. 2016-08-01
 2   Jane Doe     Adventure Works Ltd. NULL
 3   Jeff Puckett StackUnderflow       NULL
 4   Ill Gates    StackUnderflow       NULL

2 个答案:

答案 0 :(得分:6)

您必须使用NULLIF函数来避免从XML选择中弹出默认值。

  

如果两个指定的表达式相等,则返回null值。

您的查询将更改如下:

SELECT 
    t.c.value('@id',         'INT' ),
    t.c.value('Name[1]','VARCHAR(100)' ),
    t.c.value('../@dmd:name',    'VARCHAR(100)' ),
    NULLIF(t.c.value('LastDay[1]',  'DATE' ),'')
FROM @xml.nodes('/Data/Company/Employee') t(c)

有关NULLIF的详情,请查看this MSDN page

答案 1 :(得分:1)

除了techspider的非常好的答案,我想展示另一种方法:

公司上执行.nodes(),在员工上执行CROSS APPLY .nodes(),可以实现更清晰的XPath导航并避免{{1}使用的向后导航}}。在你的情况下,这可能只是为了信息,但很好的考虑:如果有一个公司没有任何员工,你会跳过整个公司,否则...(我的代码将跳过由于../@dmd.name,但你可以使用CROSS APPLY)。

对于你的实际问题:使用内部OUTER APPLY将在XQuery中执行逻辑并且应该更快......

cast as xs:date