在上一篇文章中,我已经完成了旧数据库的导入。然而,我的博客原先除了用Access数据库保存数据,也大量使用了XML存储,这借鉴自BlogEngine.NET,但后来发现XML存储弊大于利,最适合我的还是传统关系型数据库。因此,我有必要把所有非系统数据迁移到SQL Server上。这里要提一句,系统数据和用户数据是不一样的。系统数据是维持系统程序的运行所需要的数据,用户数据是用户使用系统产生的数据。系统数据包括系统设置(XML保存)、系统菜单等等,而用户数据除了博客文章,还有其创建的页面、友情链接,这些东西原先都是XML存储的,这次要统一迁移到SQL Server上。

首先,要对应XML的数据结构,在SQL Server中建立对应的表,列名和数据类型可以不一样,但要记得方便导入,不然就得用前几篇文章的技巧处理了。本文以友情链接表为例,建立的表如下:

CREATE TABLE [dbo].[FriendLink](
    [Id] [uniqueidentifier] NOT NULL,
    [Title] [nvarchar](100) NOT NULL,
    [Link] [nvarchar](300) NOT NULL,
    [OrderId] [int] NOT NULL,
 CONSTRAINT [PK_FriendLink] PRIMARY KEY CLUSTERED
(
    [Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]

除了Link字段是重命名自XML中的Url字段,其他的字段数据类型和名称都没有变化。

SQL Server 天生支持XML类型的数据,可以从一坨XML字符串中解析数据到一张表。系统内置了一个叫做sp_xml_preparedocument的存储过程,它在MSDN上的描述是:

读取作为输入提供的 XML 文本,然后使用 MSXML 分析器 (Msxmlsql.dll) 对其进行分析,并提供分析后的文档供使用。 分析后的文档对 XML 文档中的各节点(元素、属性、文本和注释等)的树状表示形式。

sp_xml_preparedocument 返回一个句柄,可用于访问 XML 文档的新创建的内部表示形式。 该句柄在会话的持续时间内有效,或者通过执行 sp_xml_removedocument 使其在句柄失效前一直有效。

用法是:

DECLARE @docHandle INT –返回的句柄
EXEC sp_xml_preparedocument @docHandle OUTPUT,
     @doc –输入的XML字符串
EXEC sp_xml_removedocument @docHandle –用完以后清理这个对象

另一个需要用到的系统函数是OPENXML。MSDN参阅:http://msdn.microsoft.com/zh-cn/library/ms186918%28v=sql.105%29.aspx

直接介绍语法和概念太抽象,所以我先贴出导入友情链接数据的脚本,然后再讲解:

(为了节省篇幅,我缩减了友情链接的数量)

 

DECLARE @doc XML

SET @doc = 
    '<?xml version="1.0"?>
	<FriendLinkData>
	  <FriendLink>
		<Id>bf6272bc-7b22-4002-bb43-a84b5997afd5</Id>
		<Title>XNA Develop</Title>
		<Url>http://www.xnadevelop.com/</Url>
		<OrderId>0</OrderId>
	  </FriendLink>
	  <FriendLink>
		<Id>7579ed1e-2a63-410c-9f5b-631c598181e9</Id>
		<Title>蜈蚣s Blog</Title>
		<Url>http://www.wgcss.com</Url>
		<OrderId>1</OrderId>
	  </FriendLink>
	  <FriendLink>
		<Id>74ebcaa9-5aff-474b-ac36-647586a8c0ee</Id>
		<Title>老徐的博客</Title>
		<Url>http://www.54peixun.com/Author/frankxulei</Url>
		<OrderId>2</OrderId>
	  </FriendLink>
	</FriendLinkData>'

DECLARE @docHandle INT

EXEC sp_xml_preparedocument @docHandle OUTPUT,
     @doc
INSERT INTO FriendLink
(
	Id,
	Title,
	Link,
	OrderId
)
SELECT Id,
       Title,
       [Url],
       OrderId
       
FROM   OPENXML(@docHandle, '/FriendLinkData/FriendLink', 2)
       WITH
       (Id UNIQUEIDENTIFIER, Title NVARCHAR(100), [Url] NVARCHAR(300), OrderId INT)

EXEC sp_xml_removedocument @docHandle

这个脚本是很容易理解的,只是有几个地方要注意。首先,OPENXML函数的最后一个参数,是有含义的。它可以是下列值之一:

字节值

说明

0

默认为“以属性为中心”的映射。

1

使用“以属性为中心”的映射。可以与 XML_ELEMENTS 一起使用。这种情况下,首先应用“以属性为中心”的映射,然后对所有未处理的列应用“以元素为中心”的映射。

2

使用“以元素为中心”的映射。可以与 XML_ATTRIBUTES 一起使用。这种情况下,首先应用“以属性为中心”的映射,然后对所有未处理的列应用“以元素为中心”的映射。

8

可与 XML_ATTRIBUTES 或 XML_ELEMENTS 组合使用(逻辑或)。在检索的上下文中,该标志指示不应将已使用的数据复制到溢出属性 @mp:xmltext

     

在我的友情链接XML中,使用的是元素保存值,而不是保存在标签的属性中。所以选择的是2。

另外一个小细节就是XML的头部,一定要写成“<?xml version="1.0"?>”不能再多写其他属性了,不然SQL是会爆的。

其他XML数据的导入方式和这个类似,不再重复讲解了。对于其他XML的导入,大家可以自己看一下MSDN的介绍。