1、使用for xml语句将数据库中的的数据生成xml文档时,有些值为null的列没有生成到xml文档;
如下图:
使用语句:
SELECT TOP 1 tp.ID, tp.machine_id, tp.plan_time, tp.product_num, tp.art_num,
tp.start_time, tp.end_time, tp.arrange_main_iden
FROM DADXDB.dbo.table_plan AS tp
ORDER BY tp.plan_time DESC
FOR XML PATH('plan_rec') ,ELEMENTS
生成xml:
<plan_rec>
<ID>18700</ID>
<machine_id>DX-5</machine_id>
<plan_time>2022-07-14T14:10:29</plan_time>
<product_num>D470219A</product_num>
<art_num>D470219A</art_num>
<arrange_main_iden>52233</arrange_main_iden>
</plan_rec>
这里不包含start_time和end_time。
如何才能让xml中包含这两列呢?
使用下面的方法:
SELECT TOP 1 tp.ID, tp.machine_id, tp.plan_time, tp.product_num, tp.art_num,
tp.start_time, tp.end_time, tp.arrange_main_iden
FROM DADXDB.dbo.table_plan AS tp
ORDER BY tp.plan_time DESC
FOR XML PATH('plan_rec') ,ELEMENTS XSINIL
生成的xml如下:
<plan_rec xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<ID>18700</ID>
<machine_id>DX-5</machine_id>
<plan_time>2022-07-14T14:10:29</plan_time>
<product_num>D470219A</product_num>
<art_num>D470219A</art_num>
<start_time xsi:nil="true" />
<end_time xsi:nil="true" />
<arrange_main_iden>52233</arrange_main_iden>
</plan_rec>
请注意上面的xml中已经包含了start_time和end_time列,另外也在开始时增加了一个namespace的定义,这些就是使用xsinil后起的效果;
2、如何从xml文档中正确读出null值到sqlserver中
上面的方法说明如何将null值生成到xml中,那么在xml中的null值如何能被正确的识别处理呢?特别时对于日期类型值,默认情况读出时,null值往往被转换为1900-1-1 00:00:00,这些不是一个很好的地方;
如,下面的xml:
<plan_rec xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<ID>18700</ID>
<machine_id>DX-5</machine_id>
<plan_time>2022-07-14T14:10:29</plan_time>
<product_num>D470219A</product_num>
<art_num>D470219A</art_num>
<start_time xsi:nil="true" />
<end_time xsi:nil="true" />
<arrange_main_iden>52233</arrange_main_iden>
</plan_rec>
(1)、使用xml的value语句:
DECLARE @tmpXml XML
SELECT @tmpXml='<plan_rec xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<ID>18700</ID>
<machine_id>DX-5</machine_id>
<plan_time>2022-07-14T14:10:29</plan_time>
<product_num>D470219A</product_num>
<art_num>D470219A</art_num>
<start_time xsi:nil="true" />
<end_time xsi:nil="true" />
<arrange_main_iden>52233</arrange_main_iden>
</plan_rec>'
SELECT @tmpXml.value('(/plan_rec/start_time)[1]','datetime')
执行结果如下:
注意这里的datetime 没有正确的被转换成null;
DECLARE @tmpXml XML
SELECT @tmpXml='<plan_rec xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<ID>18700</ID>
<machine_id>DX-5</machine_id>
<plan_time>2022-07-14T14:10:29</plan_time>
<product_num>D470219A</product_num>
<art_num>D470219A</art_num>
<start_time xsi:nil="true" />
<end_time xsi:nil="true" />
<arrange_main_iden>52233</arrange_main_iden>
</plan_rec>'
SELECT @tmpXml.value('(/plan_rec/start_time[not(@xsi:nil = "true")])[1]','datetime')
注意上面代码中在xpath部分,增加了一个属性过滤条件:[not(@xsi:nil = "true")],这样会报标识为nil的元素过滤掉,所以,会正确返回null,如下:
(2)、使用openxml语句
尝试1:
DECLARE @tmpXml VARCHAR(8000)
SELECT @tmpXml= '<plan_rec xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<ID>18700</ID>
<machine_id>DX-5</machine_id>
<plan_time>2022-07-14T14:10:29</plan_time>
<product_num>D470219A</product_num>
<art_num>D470219A</art_num>
<start_time xsi:nil="true" />
<end_time xsi:nil="true" />
<arrange_main_iden>52233</arrange_main_iden>
</plan_rec>'
DECLARE @hdoc INT
EXEC sp_xml_preparedocument @hdoc OUTPUT,@tmpXml
SELECT *
FROM OPENXML(@hdoc,'/plan_rec',2)
WITH(
ID INT ,
machine_id VARCHAR(10),
plan_time DATETIME,
product_num VARCHAR(10),
art_num VARCHAR(20),
start_time DATETIME,
end_time DATETIME,
arrange_main_iden INT
)
执行结果:
从执行结果中可以看到start_time和end_time没有被正确读取;
尝试2,在with列定义处,指明xpath路径采用类似value方法的属性过滤
DECLARE @tmpXml VARCHAR(8000)
SELECT @tmpXml= '<plan_rec xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<ID>18700</ID>
<machine_id>DX-5</machine_id>
<plan_time>2022-07-14T14:10:29</plan_time>
<product_num>D470219A</product_num>
<art_num>D470219A</art_num>
<start_time xsi:nil="true" />
<end_time xsi:nil="true" />
<arrange_main_iden>52233</arrange_main_iden>
</plan_rec>'
DECLARE @hdoc INT
EXEC sp_xml_preparedocument @hdoc OUTPUT,@tmpXml
SELECT *
FROM OPENXML(@hdoc,'/plan_rec',2)
WITH(
ID INT ,
machine_id VARCHAR(10),
plan_time DATETIME,
product_num VARCHAR(10),
art_num VARCHAR(20),
start_time DATETIME '/start_time[not(@xsi:nil = "true")]',
end_time DATETIME,
arrange_main_iden INT
)
EXEC sp_xml_removedocument @hdoc
但是这时执行,会报如下错误;
尝试3:
在使用sp_xml_preparedocument过程时,指明命名空间,如下:
DECLARE @tmpXml VARCHAR(8000)
SELECT @tmpXml= '<plan_rec xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<ID>18700</ID>
<machine_id>DX-5</machine_id>
<plan_time>2022-07-14T14:10:29</plan_time>
<product_num>D470219A</product_num>
<art_num>D470219A</art_num>
<start_time xsi:nil="true" />
<end_time xsi:nil="true" />
<arrange_main_iden>52233</arrange_main_iden>
</plan_rec>'
DECLARE @hdoc INT
EXEC sp_xml_preparedocument @hdoc OUTPUT,@tmpXml,'<plan_rec xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"/>'
SELECT *
FROM OPENXML(@hdoc,'/plan_rec',2)
WITH(
ID INT ,
machine_id VARCHAR(10),
plan_time DATETIME 'plan_time[not(@xsi:nil = "true")]' ,
product_num VARCHAR(10),
art_num VARCHAR(20),
start_time DATETIME '/start_time[not(@xsi:nil = "true")]',
end_time DATETIME '/end_time[not(@xsi:nil = "true")]',
arrange_main_iden INT
)
EXEC sp_xml_removedocument @hdoc
执行结果如下:
这个才是理想的解析结果
3、使用select for xml查询生成xml后,直接使用value函数报错问题;
如下面语句:
SELECT
(
SELECT TOP 1 tp.ID, tp.machine_id, tp.plan_time, tp.product_num, tp.art_num,
tp.start_time, tp.end_time, tp.arrange_main_iden
FROM DADXDB.dbo.table_plan AS tp
ORDER BY tp.plan_time DESC
FOR XML PATH('plan_rec') ,ELEMENTS XSINIL
).value('(/plan_rec/start_time)[1]','datetime')
会报下图错误:
这时我们只需要对上面sql语句做如下修改就可以解决这个问题:
SELECT
(
SELECT TOP 1 tp.ID, tp.machine_id, tp.plan_time, tp.product_num, tp.art_num,
tp.start_time, tp.end_time, tp.arrange_main_iden
FROM DADXDB.dbo.table_plan AS tp
ORDER BY tp.plan_time DESC
FOR XML PATH('plan_rec') ,ELEMENTS XSINIL,TYPE
).value('(/plan_rec/start_time)[1]','datetime')
注意:上面代码中增加了Type关键字,表示当前这个查询结果是一个xml类型,这时就不会报错了。
对于上面的内容,对于有特殊要求时可以按照上面的方式处理;,如果没有的话完全可以使用sqlserver的默认行为处理:
1、sqlserver中的null数据不会生成到xml中;
2、在使用value/openxml读取xml时,这些的对应信息也自然会是null
3、如果sqlserver中有值,那么就会出现到xml中;
4、这样后续读取时,也自然会读取到。
当然,对于一些非字符串类型,sqlserver会有一些默认值,而这些默认值往往不是我们想要的,所以简单一点,就是将所有的类就是声明成字符串类型,这样如果有null值时,会解释为空串,这样可能会更符合我们的理解习惯。