关于标签的的效率和准确性研究

我的标签表结构设计还有查询。的确是存在一些问题。当初我敢说。TAG的想法刚被流传的时候。我就开始注意到这个功能了。所以在国内主流的PHP开发的BLOG程序中,是第一个支持此功能的。以前有人和我说过。现在又有高手提起。我的标签的确存在着两个问题。感谢yake.chen@dudu-inc.com

Tag功能的表结构设计存在一定问题。目前是通过模糊搜索查询方式来匹配标签,进而通过标签来匹配到文章。这个实现方式有两个致命的缺点:

  1. 大数据量下标签搜索的效率问题。
  2. 标签匹配的准确性问题。(所记录的标签出现条数并非模糊搜索显示出的条数)

的确。本来刚有人和我提出的时候,我就把改善TAG表列入计划中。可是后来忘记了。结果这次发布的测试版后。高手又提出了这个问题。才让我想起来。的确要改善。

因为标签是个 多对多的结构体系  一篇文章可以有多个标签,一个标签可以对应多篇文章。所以一般性的解决办法是为标签结构使用两个表来描述。

你现在的标签表算一个,另外一个记录标签和文章标签的关系。

比如建立一个表tag_relation 三个字段  tid, article_id, tagid每插入数据库一个标签的时候同时更新这两个表。tag_replation的tagid字段对应你的tags表的tagid,tag_relation 表的tid是个自增的主键记录每次入库的tag。article_id是此tag对应的文章id,这个表记录的基本只是数字型的ID值所以存储空间消耗不大且实现了高效精确匹配的问题。

我自己也想了想关于标签的表结构的问题。如果不用另外一个表记录关联字段,而是用tag表的一个字段来保存一个关联ID,也是一个比较好的办法吧??比如:

Field tagid tag usetime aids
Value 1 angel 5 1,3,4,8,5

这样的话,我必定要先从URL获取name,也就是:

http://www.4ngel.net/blog/angel/?action=tag&item=angel 
select aids from tags where tag='angel'    1 query

然后再来
select * from articles where articleid in (aids)   2 queries

这样效率问题也解决了。准确性也解决了。不知道大家还有没有其他的看法。如果使用另外一个表来放关联字段。那不是还要来一次LEFT JOIN?

希望各位多多交流技术。

Tags: 标签, 效率, tag

« 上一篇 | 下一篇 »

相关文章

访客评论

其实也是类似于我现在的搜索引擎了。
补充一句:TAGS非常重要,现在至少GOOGLE在搜索中已经对其着重考虑了,尤其是现在网络上越来越多的WEB形式:BLOG,从另一个方面解释TAGS是BLOG被搜索收录的引子,连接点!
!important
在firefox里显示有些问题
对于这种多对多的关系表,还是用双表关联好.我以前也是图方便只用一个字段来记关联系,结果很不爽,数据量大后修改又不是很方便.

如果一个TAG的使用率很高,那这个字段就要足够大才能存储(MYSQL的记录分配不知是不是预分配空间,如果是那其他使用率低的就很浪费空间了)

而且管理也很不方便,如某篇文章删除后,应该要更新字段信息(若用你那句SQL则不会有问题)

查找某篇文章的所有tag呢,你SQL怎么写?

至于效率,还是得做个对比测试最好.
我觉得的确存在一个空间问题。因为考虑到使用多的标签,我字段是text类型。对于使用少的标签。存储的确是浪费。

如果使用多表查询的话,要考虑优化问题。比如要查找指定文章所有的tag,就把keywords字段读取出来:

--------------------------------------
$tagdb = explode(",", $article['keywords']);
$tags = $comma = '';
//用单引号把标签包起来再用逗号连接所有标签
for($i=0; $i<count($tagdb); $i++) {
    $tags .= $comma."'".$tagdb[$i]."'";
    $comma = ',';
}
//查询这些标签的aids并连接起来
$query = $DB->query("SELECT aids FROM tags WHERE tag IN (".$tags.")");
$relaids = $comma = '';
while ($tag = $DB->fetch_array($query)) {
    $relaids .= $comma.$tag['aids'];
    $comma = ',';
}
// 分开这些ID并清除重复值的单元
$relids = explode(",", $relaids);
$relids = array_unique($relids);

$related_tatol = count($relids);
$relids = implode(",",$relids);
if ($related_tatol > 0 && $relids != $articleid) {
    $query = $DB->query("SELECT title FROM articles WHERE visible='1' AND articleid IN (".$relids.") AND articleid <> '".$articleid."' ORDER BY articleid DESC LIMIT 10);
    $titledb=array();
    while ($title = $DB->fetch_array($query)) {
        echo $title['title']."<br />";
    }
    unset($title);
    $DB->free_result($query);
}
--------------------------------------

不知道你怎么看这样处理的?这个部分也就是我的相关文章的部分。
原来是用in来配匹关键字的,我目前比较倾向于再键多个表来存放TAG ID和article ID的关联.
虽然据说in比like效率高,不过怎么高也是字符串匹配,总不会比TAG.TAG_ID=ARTICLE.TAG_ID的数字查询来得快吧,而且这好像也是大部份中文分词索引的做法.

个人BLOG的数据量较少,所以效率不是很明显,如果是做BLOG门户或多人BLOG,那可能会明显些.

用关联表对TAG的关联更新最简单就是一次过DELETE,再一次过INSERT,因为表结构简单,碎片很小,速度也快.
其实IN的速度是非常快的。看看MYSQL的手册,

expr IN (value,...)
如果 expr 是 IN 列表中的作一值,它将返回 1,否则返回 0。如果所有的值均是常数,那么所有的值被依照 expr 的类型进行计算和排序。然后以一个二进制搜索方式完成项目的搜索。这就意味着,如果 IN 列表完全由常数组成,IN 将是非常快的。如果 expr 是一个字母大小写敏感的字符串表达式,字符串比较将以大小写敏感方式执行:
mysql> SELECT 2 IN (0,3,5,'wefwf');
        -> 0
mysql> SELECT 'wefwf' IN (0,3,5,'wefwf');
        -> 1

从 MySQL 4.1 开始(符合 SQL-99 标准),如果左手边的表达式是 NULL,或者在列表中没有发现相匹配的值并且列表中的一个表达式是 NULL,IN 均返回 NULL。

expr NOT IN (value,...)
等同于 NOT (expr IN (value,...))。

而且PHPWIND和DISCUZ都用了比较多的IN.现在的速度和原来的比,还是很明显的。大概和我用LIKE的时候,快了0.005000秒左右。我想单表查询怎么都要比多表查询快吧?而且操作也很方便。
"...如果 IN 列表完全由常数组成,IN 将是非常快的..."

常数当然快了,即使是用like 也不见得慢,如
select 'a' like '%what the hell%';

但实际应用是查表查字段,如
select tag_id from table where artist.tag_id in (1,2,3)

多表的确在联合查询时比较麻烦,如要在查文章时,同时查询文章所有的TAG,特别是对不支持子查询的低版MYSQL不利.

只能说各有利弊吧
不过我觉得,虽然是如果多一个关联表,感觉多一个表还是不怎么爽,心理原因吧。呵呵。真的是各有利弊。我也学到了一点东西。谢谢啦。
这个软件的后台登陆怎么在首页上没有显示出来啊! 我每次登陆我的博客发表日志 都得从地址栏里输入.../blog/admin/admincp.php 才能进入我的后台,好麻烦的!不知道各位是不是也和我一样这么做呢? 有没有简单的办法? 请指教!谢了!
Post by 劲草 on 2006-05-05, 12:24 PM #10
Total:22123Next ›

发表评论

评论内容 (必填):

My E-mail