关于标签的的效率和准确性研究
Written by angel on 2006, March 30, 4:33 AM. 技术相关
我的标签表结构设计还有查询。的确是存在一些问题。当初我敢说。TAG的想法刚被流传的时候。我就开始注意到这个功能了。所以在国内主流的PHP开发的BLOG程序中,是第一个支持此功能的。以前有人和我说过。现在又有高手提起。我的标签的确存在着两个问题。感谢yake.chen@dudu-inc.com。
Tag功能的表结构设计存在一定问题。目前是通过模糊搜索查询方式来匹配标签,进而通过标签来匹配到文章。这个实现方式有两个致命的缺点:
- 大数据量下标签搜索的效率问题。
- 标签匹配的准确性问题。(所记录的标签出现条数并非模糊搜索显示出的条数)
的确。本来刚有人和我提出的时候,我就把改善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非常重要,现在至少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 才能进入我的后台,好麻烦的!不知道各位是不是也和我一样这么做呢? 有没有简单的办法? 请指教!谢了!
发表评论