blog 合并、Post ID 重排以及数据库整理

真是很久都没有用 blog 这个词了,很久以前可是不绝于耳的说,现在总觉得听起来很别扭。这几天做了一个艰难的决定,把以前存放在 donews 和 blogger 上的老日志合并到本窝里来。通过艰苦卓绝的操作,最终完成了预期的目标,从而在历史上第一次实现了“一个人只有一个 blog”的伟大构想。好了废话就不说了。谈点过程中的一些处理手段。

首先是导出导入。以前主要是在 donews 上写,然后在 blogger 上做个备份,所以 blogger 上只有为数不多的几篇额外的日志。因此导出导入主要在 donews 上完成,然后手动把 blogger 上的抄过来。donews 在很久以前也改成了 wordpress 的后台,因此导出导入工作很容易,就不仔细说了。

麻烦的是导入以后,有好几个问题:

  1. 写日志时间跨度有点大,所以写作格式的风格有不少变化,再加上 donews 迁移到 wordpress 过程中保留了大量以前随手添加的样式和标签,看起来有点乱七八糟的。这个只能慢慢改,是个体力活。
  2. 导入过程中如果单篇文章长度较长,则很可能导入不全。这个带来的最大的问题是标签不闭合,于是页面也就错乱了。对着以前的 blog 找茬吧,也是体力活。
  3. 以前 blog 里的图片链接全部要更新,有些图片还丢失了。这个基本就没有什么好的解决办法了,把那些还能显示出来的下载上传到本窝,其他的就直接删掉图片链接。好在写的 blog 以文字为主,丢失图片影响不大。
  4. 导入后数据库的 post id 出现了暴涨,从正常的 2000 左右涨到了快 1250000 了。主要原因是导出的文件里保留了 donews 上原始的 id,而导入的时候居然也一并导入了。这也不是什么大问题,本窝的链接也都是 ID 无关的,但是这些巨大的数字看着很烦人。
  5. 导入过后 wp_posts 表里的 guid 字段有大量的 donews 链接,也需要修改。

此外,为了和本窝融合,老文还需要逐一添加 tags,并且翻译标题以便有个简洁的 post slug。加上上面头三条体力活,任重道远啊。下面主要说一下如何压缩 post id,并且如何快速修改 guid,这两个就是技术活了,善用工具可以在很短的时间完成。

1. 重排 Post ID

这两个工作之前不少人搞过了,比如压缩 post id 这个,简单搜索一下出来的中文日志主要就有三个:《修正WordPress的ID不连续问题》、《WordPress完美解决文章ID不连续问题》以及《WordPress文章ID之完美重排》。头两篇文章的主要意图是禁用 wordpress 的 revisions,重排 post id 只是附加的操作,我觉得 revisions 挺好的,所以只重排。而第三篇则是以重排为中心,重新写了处理程序,减少了出错的可能性。仔细研究了一下这几篇里头提到的程序以及 wordpress 里头各个相关表格的结构,觉得这个方法在 wordpress 3.1 下会有问题:

  1. 三个程序里头都没有修改 parent postpost_parent 字段,所以有上下级关系的页面会出现混乱。最突出的是文章附件,估计媒体管理会因此出故障。设计了层次关系的 page 页也会难逃厄运。另外那些 revisions 和 autosave 自然也是混乱的。
  2. 没有修改 wp_postmeta 表中 meta_key 字段是 _thumbnail_id 的记录的 meta_value 字段。这种记录的 meta_value 字段保存的是文章的 featured image 对应的图片附件的 post_id。所以没有修改的话,这些 featured image 自然也就乱套了,大多数 featured image 都会丢失。
  3. 修改 wp_term_relationships 表的时候没有考虑 object_id 里头包含的 link_id 与 post_id 存在重复的情况,都一股脑处理掉了,于是链接管理也会出问题。
  4. wp_term_relationships 表里可能存在大量的无效项目,比如我自己的表居然查出来 506 个已经不存在的 post_id。重排的时候可能会因为记录唯一性的关系而出错,也许需要做一下预处理。当然这只是我的猜测,也许允许重复也说不定。为了数据库的干净,我倾向于先清理一下。

1.1 清理工作

开工之前需要做点清理,比如把 revisions 删掉,上面头两篇文章里头说了两种办法。个人觉得插件的方法简单些。删掉过后,就可以搞上面第三条的预处理了。主要思路是从 wp_posts 表里头查出所有存在的 ID,包括 post、page、菜单、附件等很多项目的。然后在 wp_term_relationships 表里查不在这些 id 内的 object_id。需要排除 link gallery 的相关 object_id,具体哪些是 link gallery 的,可以先查 wp_term_taxonomy 表里头的 taxonomy 字段,找到 link gallery 对应的 term_taxonomy_id。我没有用 link 功能,所以 link gallery 的term_taxonomy_id 是初始的 2 号,于是下面列出来的程序只排除了这一项。这程序是我抱着 php 手册拼出来的,所以不敢直接在这里头删记录,查出来列表过后放到 phpMyAdmin 里头删掉。如果觉得不保险,其他的数据表也可以用类似的方法查一查看。


<?php

//connect to the database
mysql_connect ( "host", "user", "password" );
mysql_select_db ( "db_name" );

// select all existing ids from table wp_posts
$sql = "select ID from wp_posts order by ID asc";
$result = mysql_query ( $sql ) or die ( $sql );
$str_rows = '';
while( $row = mysql_fetch_array ( $result ) ) {
	$str_rows .= strval ( $row[ "ID" ] ) . ',';
}
$str_rows = rtrim ( $str_rows, ',' );

// select object_ids that are not in the list of existing ids in
// table wp_posts and that are not assosiated with a link gallery.
// In my wordpress the term_taxonomy_id of link galleries is 2.
$sql = "select object_id from wp_term_relationships where object_id not in ($str_rows) and term_taxonomy_id <> 2";
$result = mysql_query ( $sql ) or die ( $sql );
$str_rows = '';
while ( $row = mysql_fetch_array ( $result ) ) {
	$str_rows .= strval ( $row[ "object_id" ] ) . ',';
}
$str_rows = rtrim ( $str_rows, ',' );

echo 'Non existing object_ids in table wp_term_relationships are as followed:' . '<br />';
echo $str_rows;

?>

1.2 重排 Post ID

完事过后就是重新排序 post id 了。思路就是前面三篇里头的那种,从 wp_posts 表里都找到所有的 ID,然后在几个相关表里搜索这些 ID,然后改掉。第一篇里头为了防止改的时候重号,从一个巨大的 100001 开始编号,最后还忘了打开 ID 的自动增加选项,所以第三篇文章对这个做了改进,第一遍从 100001 开始编号,完事过后又从 1 重新编一次,最终再打开自动增加选项,这样就比较完美了。我在第三篇的基础上添加了项查询,把 parent postpost_parent 和相关 meta_value 也一并改掉。同时修改了对 wp_term_relationships 的更新查询,添加了排除 link gallery 的判断。程序如下:


<?php

//connect to the database
mysql_connect ( "host", "user", "password" );
mysql_select_db ( "db_name" );

// first reorder starts from 1250001
$original_id = 1250001;

$No = 0;
while ( $No < 2 ) {
	$sql = "select ID from wp_posts order by post_date asc;";
	$result = mysql_query ( $sql ) or die ( $sql );

	while ( $row = mysql_fetch_array ( $result ) ) {

		// change the ID for post
		$sql = 'update wp_posts set id = ' . $original_id . ' where id = ' . $row[ "ID" ] . ';';
		mysql_query ( $sql );

		// change the ID in post_parent
		$sql = 'update wp_posts set post_parent = ' . $original_id . ' where post_parent = ' . $row[ "ID" ] . ';';
		mysql_query ( $sql );

		// change the meta_value in postmeta
		$sql = 'update wp_postmeta set meta_value = ' . $original_id . ' where meta_key = "_thumbnail_id" and meta_value = ' . $row[ "ID" ] . ';';
		mysql_query ( $sql );

		// change the object_id except link gallerys
		$sql = 'update wp_term_relationships set object_id = ' . $original_id . ' where term_taxonomy_id <> 2 and object_id = ' . $row[ "ID" ] . ';';
		mysql_query ( $sql ) or die ( mysql_error ( ) );

		// change the ID for postmeta
		$sql = 'update wp_postmeta set post_id = ' . $original_id . ' where post_id = ' . $row[ "ID" ] . ';';
		mysql_query ( $sql );

		// change the ID for comments
		$sql = 'update wp_comments set comment_post_ID = ' . $original_id . ' where comment_post_ID = ' . $row[ "ID" ] . ';';
		mysql_query ( $sql );

		$original_id++;
	}

	// second reorder starts from 11
	$original_id = 11;
	$No++;
}

mysql_query ( "ALTER TABLE wp_posts auto_increment = 1 " );

?>

前面说到导入文章后 id 暴涨,所以上面第一次重新排序的起始值我选了 1250001,这只比我最大的 id 大了一点点…至于第二次为啥从 11 开始排,这完全是个人喜好了,我喜欢在前面预留一点点空间,心里感觉安稳些。

2. 修改 guid

完事过后就是整理数据库了。主要是改正那些包含 donews 链接的 guid。之前有篇讲数据库优化的文章里头提到篇文章,说可以用查找替换的方式。可这回是导入前忘了把永久链接改成本窝的样式,导入过后也就没有办法查找替换了。而且上面重排 id 过后,以前的 http://www.example.com/?p=ID 样子的 guid 没有办法替换,基本上还不如重写。所以参考了这个人的帖子《Updating guid in Your WordPress Database》,把所有的 post 的 guid 都改成了 http://www.example.com/?p=ID 的样子,把 page 也改成了 http://www.example.com/?page_id=ID。改的过程中发现这位同志忘了把导航菜单的也一并改掉,也许是因为他没有用导航菜单吧。导航菜单 guid 的样子和 post 是一样的。修改的时候因为查询语句很简单,就直接在 phpMyAdmin 里弄了,具体来讲如下:

post:

update wp_posts
set guid = concat('http://www.example.com/?p=',ID)
where post_status = 'publish'
and post_type = 'post';

page:

update wp_posts
set guid = concat('http://www.example.com/?page_id=',ID)
where post_status = 'publish'
and post_type = 'page';

nav_menu_item:

update wp_posts
set guid = concat('http://www.example.com/?p=',ID)
where post_status = 'publish'
and post_type = 'nav_menu_item';

其中 post 和 page 的 post_status 还有 private 和 trash 的,也该一并改掉。

大概就这些了。评论里头自己的回复如果链接地址不对也该改一改,这个用查找替换就搞定了。搞完过后出问题的主要是导航菜单,可能还有一些地方没有改到。不过一时冲动到后台把乱掉的菜单重做了一次,忘记了先看数据库找找出问题的原因。所以也就说不出个所以然了。

技术活干完了就是体力活了,想着就头疼,不过为了本窝的统一大业,还是要加把劲呢。

聊一聊吧

2条评论

  1. 不得不说这是 Life saver。

    我也是对 Post ID 很有要求的人。不过苦于以前的 Post 已经被 Google 收录不少了,只能在新建的两个博客(自己笔记本电脑上 LAMP + WordPress 记日记用)和帮女友搭的博客上实践「连续 Post ID」大法了。

    1. 哈哈,说来惭愧,现在我基本看不懂我写的是啥了,所谓学得快忘得也快。以前我动了脑筋的成果能帮上忙感觉真好^ω^

说点啥呗

您的电子邮箱地址不会被公开。 必填项已用*标注