上篇文章留下了一个话题,关于标签库的导入。因为设计的变化,标签库无法从Keywords字段中直接导了。
首先,分析一下新表的结构。Tag表是用来保存所有标签的。Tag.Id是uniqueidentifier类型的主键,它将被PostTag表关联,来描述多对多的结构。Tag.Name是标签名称。这是可以从老数据的Blog.Keywords字段中获得的。
所以,第一步,是要从Keywords字段提取信息,插入到Tag表中。随后再处理关联问题。
老数据库里的Keywords字段中保存的,是以英文逗号分隔的文章标签。比如“ASP.NET,CSS,HTML”就表示这篇文章拥有3个标签,分别是ASP.NET、CSS和HTML。首先,我需要解析原先的Keywords字符串,将以逗号分隔的内容解析出来。但SQL Server没有提供Split函数,所以需要自己写一个:
CREATE FUNCTION [dbo].[udf_Split] ( @SourceSql VARCHAR(8000), @StrSeprate VARCHAR(10) ) RETURNS @temp TABLE(a VARCHAR(100)) AS BEGIN DECLARE @i INT SET @SourceSql = RTRIM(LTRIM(@SourceSql)) SET @i = CHARINDEX(@StrSeprate, @SourceSql) WHILE @i >= 1 BEGIN INSERT @temp VALUES ( LEFT(@SourceSql, @i -1) ) SET @SourceSql = SUBSTRING(@SourceSql, @i + 1, LEN(@SourceSql) -@i) SET @i = CHARINDEX(@StrSeprate, @SourceSql) END IF @SourceSql <> '\' INSERT @temp VALUES ( @SourceSql ) RETURN END
这个函数可以很好的进行分割处理:
但旧数据库中的记录是一条条的keyword字符串,并不是完整拼接在一起的。而且由于T-SQL的语法特性,除非用游标(如此高级的东西我还没用过),不然是不能直接像C#那样foreach循环处理的。因此,我需要先把所有的keywords的筛选结果都拼成一个字符串:
DECLARE @allKeywordString NVARCHAR(MAX) DECLARE @sql AS VARCHAR(8000) SELECT @sql = '' SELECT @sql = @sql + Keyword + ',' FROM BlogSQL.dbo.Blog SELECT @allKeywordString = @sql
这段代码的执行结果如下:
但有个小小的瑕疵,即最后一个Keyword之后会有一个逗号“,”。这个后面要记得处理掉。最后,插入Tag表的脚本如下:
INSERT INTO Tag ( NAME ) SELECT DISTINCT a FROM dbo.udf_Split(@allKeywordString, ',') ORDER BY a DELETE FROM Tag WHERE [Name] = ''
现在,Tag表里已经有数据了。下一步是要做文章和标签多对多的关联表PostTag。因为Post.Id已经和原来的Blog.Id关联不上了,所以现在定位文章的办法是按标题查找(不会有两篇文章标题一样)。找到以后,筛选它的keywords,使用udf_Split函数分割后,将该文章的Post.Id即它的标签对应的Id存入数据库(查询Tag表)。处理单篇文章的存储过程如下:
ALTER PROCEDURE [dbo].[MergePostTag](@PostId UNIQUEIDENTIFIER) AS BEGIN INSERT INTO PostTag ( PostId, TagId ) SELECT @PostId, t.Id FROM Tag t WHERE t.Name IN (SELECT DISTINCT a FROM dbo.udf_Split( ( SELECT b.Keyword FROM BlogSQL.dbo.Blog b INNER JOIN EdiBlog.dbo.Post p ON p.Title = b.Title WHERE p.Id = @PostId ), ',' )) END GO
接下来要对所有文章的Id都进行导入操作。但这次不能用SQL。如果这样写,是要爆的:
这仍然是由于T-SQL的语法限制。不可以将一个函数或存储过程的返回结果放在SELECT,IN等语句当中。尽管这看上去是一种自然的表达式。关于这个问题可以参考:http://stackoverflow.com/questions/653714/how-to-select-into-temp-table-from-stored-procedure 但我觉得拼接字符串不是我能接受的解决办法。
所以,我码了个console程序用来批量导数据:
static void Main(string[] args) { Console.WriteLine("Inserting Data to PostTag, please fuck waiting."); using (EdiBlogEntities context = new EdiBlogEntities()) { int successCount = 0; int fuckedCount = 0; foreach (var item in context.Post.Select(p => p.Id)) { if(context.MergePostTag(item) > 0) { successCount++; Console.WriteLine("Successfully merge tag with postId " + item.ToString()); } else { fuckedCount++; Console.WriteLine("Merge tag fucked with postId " + item.ToString()); } } Console.WriteLine("Work complete, {0} success, {1} fucked", successCount, fuckedCount); } }
运行结果如下:
至此,标签库数据导入完成。下一篇将介绍XML数据的导入。