在上一篇文章中,我已经完成了旧数据库的导入。然而,我的博客原先除了用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的介绍。