常常遇到需要向SQL Server插入批量数据,然后在存储过程中对这些数据进行进一步处理的情况。存储过程并没有数组、列表之类的参数类型,使用XML类型可妥善解决这个问题。
不过,SQL Server2005对标准xml的支持不足,很多地方需要特别处理。举一个例子说明一下。
这个场景是往存储过程里传递一个xml序列化了的List<Model>。
1.Model的代码如下,这是一个实体类
public
class
Model
{
///
<summary>
///
UIN
///
</summary>
[XmlElement(
"
UIN
"
)]
public
long
UIN {
get
;
set
; }
///
<summary>
///
昵称
///
</summary>
[XmlElement(
"
Name
"
)]
public
string
Name {
get
;
set
; }
///
<summary>
///
头像
///
</summary>
[XmlElement(
"
Img
"
)]
public
string
Img {
get
;
set
; }
///
<summary>
///
访问时间
///
</summary>
[XmlElement(
"
VisitTime
"
)]
public
DateTime VisitTime {
get
;
set
; }
}
然后我们需要将这个List<Model>序列化成一个xml的字符串。但是SQL Server对xml的命名空间识别是有问题的,.net默认的序列化会出现xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd= http://www.w3.org/2001/XMLSchema
有网友给出了一个完美序列化Sql Server2005支持的xml的类(参考 http://www.cnblogs.com/prime/archive/2012/10/11/SQLXML.html ):
public
static
class
DbXml
{
private
static
readonly
XmlSerializerNamespaces Namespaces =
new
XmlSerializerNamespaces();
static
DbXml()
{
//
去掉 xmlns:xsi="
http://www.w3.org/2001/XMLSchema-instance
" xmlns:xsd="
http://www.w3.org/2001/XMLSchema
"
Namespaces.Add(
string
.Empty,
string
.Empty);
}
///
<summary>
///
把一个对象序列化成一个Xml字符串
///
</summary>
///
<typeparam name="T"></typeparam>
///
<param name="obj"></param>
///
<returns></returns>
public
static
string
SerializeXml<T>
(T obj)
{
XmlSerializer serializer
=
new
XmlSerializer(
typeof
(T));
using
(MemoryStream stream =
new
MemoryStream())
{
serializer.Serialize(stream, obj, Namespaces);
return
Encoding.UTF8.GetString(stream.ToArray());
}
}
public
static
T DeserializeXml<T>(
string
obj)
{
XmlSerializer serializer
=
new
XmlSerializer(
typeof
(T));
using
(StringReader reader =
new
StringReader(obj))
{
return
(T)serializer.Deserialize(reader);
}
}
}
使用的时候只需要:string xml = DbXml.SerializeXml<List<QQVisitorXml>>(list) 即可获取序列化后的xml字符串:
<?
xml version="1.0"
?>
<
ArrayOfModel
>
<
Model
>
<
UIN
>
0
</
UIN
>
<
Name
>
name0
</
Name
>
<
Img
>
img0
</
Img
>
<
VisitTime
>
2009-07-17T00:00:00-05:00
</
VisitTime
>
</
Model
>
<
Model
>
<
UIN
>
1
</
UIN
>
<
Name
>
name1
</
Name
>
<
Img
>
img1
</
Img
>
<
VisitTime
>
2009-07-17T00:00:00-05:00
</
VisitTime
>
</
Model
>
<
Model
>
<
UIN
>
2
</
UIN
>
<
Name
>
name2
</
Name
>
<
Img
>
img2
</
Img
>
<
VisitTime
>
2009-07-17T00:00:00-05:00
</
VisitTime
>
</
Model
>
</
ArrayOfModel
>
2.存储过程里,读取xml到一个临时表#temp里:
select
c.value(
'
(UIN)[1]
'
,
'
varchar(30)
'
)
as
uin,
c.value(
'
(Name)[1]
'
,
'
varchar(50)
'
)
as
Name,
c.value(
'
(Img)[1]
'
,
'
varchar(200)
'
)
as
Img,
c.value(
'
(VisitTime)[1]
'
,
'
datetime
'
)
as
VisitTime
into
#
temp
from
@strxml
.nodes(
'
//Model
'
) T(c)
--
@strxml是存储过程的xml参数
然后就可以对#temp按照普通表进行进一步处理。
我们试着执行这个存储过程。嗯?出错了?!
3.原来,XML的时间标准格式是”年-月-日T时:分:秒-时区” SQL Server2005不支持时区,所以它也不能支持xml的时间格式(倒是支持年-月-日T时:分:秒)。这个问题在SQL server 2008中得到改进,完整支持了xml的时间格式。但是我们数据库是2005,没办法,得想个办法解决。解决办法是把时间字转成字符串,然后截取 年-月-日T时:分:秒,最后再加上东八区的时区数,这样sql修正为:
select
c.value(
'
(UIN)[1]
'
,
'
varchar(30)
'
)
as
uin,
c.value(
'
(Name)[1]
'
,
'
varchar(50)
'
)
as
Name,
c.value(
'
(Img)[1]
'
,
'
varchar(200)
'
)
as
Img,
dateadd
(hour,
8
,
convert
(
datetime
,
left
(t.c.value(
'
(VisitTime)[1]
'
,
'
varchar(30)
'
),
19
),
127
))
as
VisitTime
into
#
temp
from
@strxml
.nodes(
'
//Model
'
) T(c)
--
@strxml是存储过程的xml参数
本地测试,成功!
4.放到服务器上测试,执行倒是成功了,可以一查看数据,又出问题了!服务器上插入数据表的时间,和我本地测试数据库的时间,相差8个小时!本地开发环境是windows8,服务器是windows server 2008。开发环境和服务器环境有差异,导致本地获取xml带时区,服务器不带时区。
过于依赖环境,就太危险了!果断放弃时间格式,修改Model中时间为字符串:
public
class
Model
{
///
<summary>
///
UIN
///
</summary>
[XmlElement(
"
UIN
"
)]
public
long
UIN {
get
;
set
; }
///
<summary>
///
昵称
///
</summary>
[XmlElement(
"
Name
"
)]
public
string
Name {
get
;
set
; }
///
<summary>
///
头像
///
</summary>
[XmlElement(
"
Img
"
)]
public
string
Img {
get
;
set
; }
///
<summary>
///
访问时间
///
</summary>
[XmlIgnore]
//
xml序列化时跳过
public
DateTime VisitTime {
get
;
set
; }
[XmlElement(
"
VisitTime
"
)]
public
string
XVisitTime
{
get
{
return
this
.VisitTime.ToString(
"
yyyy-MM-dd HH:mm:ss
"
); }
set
{
this
.VisitTime =
DateTime.Parse(value); }
}
}
在存储过程中把这个时间字符串转换成时间:
select
c.value(
'
(UIN)[1]
'
,
'
varchar(30)
'
)
as
uin,
c.value(
'
(Name)[1]
'
,
'
varchar(50)
'
)
as
Name,
c.value(
'
(Img)[1]
'
,
'
varchar(200)
'
)
as
Img,
convert
(
datetime
,c.value(
'
(VisitTime)[1]
'
,
'
varchar(30)
'
))
as
VisitTime
into
#
temp
from
@strxml
.nodes(
'
//Model
'
) T(c)
Ok。所有问题都解决了,畅快。

