上篇文章留下了一个话题,关于标签库的导入。因为设计的变化,标签库无法从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数据的导入。