【问题标题】:tags in mysql + phpmysql + php中的标签
【发布时间】:2009-11-06 19:16:48
【问题描述】:

有没有处理数据库中标签的通用方法?

我正在考虑使用带有管道的 tinytext。 我认为添加另一个表并使用 ID 可能会使其变得更加复杂而收效甚微。

你最喜欢的方法是什么?

在表中进行查询以查找匹配多个或单个标签的结果的正确方法是什么?

谢谢

【问题讨论】:

  • 您也可以考虑对您的标签进行 JSON 编码。

标签: php mysql tags


【解决方案1】:

实现一个简单的 N:N 关系。

项目:
-id
-名称
-标题
-foo
-酒吧

ItemTagRelations:
-Fkey itemId
-Fkey tagId

标签:
-id
-名称
-等

【讨论】:

    【解决方案2】:

    我会在这里散布一些异端。

    出于可扩展性的原因,包括本网站在内的大男孩正在使用非规范化模式作为标签,在每行的文本类型字段中存储逗号、竖线或空格分隔的标签,并为带有计数的标签存储单独的表格。在插入或更新项目时,只需检查添加或删除了哪些标签并相应地更新计数(分解为新旧标签字符串数组并执行 array_diff() )。

    现在您可以通过简单的SELECT * FROM tags 以廉价的方式显示带有计数的标签云,无需花哨的查询。要查找标有给定名称的项目,只需执行LIKE '%TAG%',这对于小流量网站(比如每天少于 100k 的页面浏览量)和小型数据集(比如少于 100k 的记录)非常有效。除此之外,您可以使用全文搜索来加快速度,并最终使用 Lucene 或 Sphinx 等合适的搜索引擎。

    在 SO 上查找相关标签也很容易(Kohana 特定代码,基于 LIKE,MySQL 特定):

    $tags = array('foo', 'bar');
    
    private function get_related_tags( $tags )
    {
        ## Get db entries with specific tags and build array with counts
    
        ## is it cached already? ------------------------------------------------
        $this->cache = Cache::instance();
        $tags = array_filter( array_flip(array_flip($tags)) );
        sort($tags);
        $cache_name = implode('', $tags);
        $cache = $this->cache->get( $cache_name );
    
        if( $cache )
         return $cache;
    
        ## not cached, fire up ---------------------------------------------------
    
        $db = Database::instance();
    
        ## count tagged items ----------------------------------------------------
    
        // build like string
        $like = array();
        foreach( $tags as $tag )
           $like[] = "tags LIKE '%$tag%'";
    
        $like = implode(' AND ', $like);
    
        // get counts
        $count = $db->query("SELECT count(id) AS count FROM `articles` WHERE $like")->current()->count;
    
        ## check what tags are related ------------------------------------------
    
        $offset = 0;
        $step = 300;
    
        $related_tags = array();
    
        while( $offset < $count )
        {
            $assets = $db->query("SELECT tags FROM `articles` WHERE $like ORDER BY id ASC LIMIT $step OFFSET $offset");
    
            foreach($assets as $asset)
            {
                // tags 
                $input = explode( ' ', trim($asset->tags) );
                foreach( $input as $k => $v )
                {
                     if( $v == ''){
                         //do nothing, shouldnt be here anyway
                     }
                     elseif( array_key_exists($v, $related_tags) ){
                         $related_tags[$v]++;
                     }
                     else{
                        $related_tags[$v] = 1;
                     }        
                }
            }
            $offset += $step;
        }
    
        // remove already displayed from list
        foreach( $tags as $tag )
            unset( $related_tags[$tag] );
    
        ksort($related_tags);
    
        // set cache 
        $this->cache->set( $cache_name, array($related_tags, $count), 'related_tags_counts', 0);
    
        return array($related_tags, $count);
    }
    

    这并不便宜,所以我一直缓存给定标签集的计数,直到我对文章表中的标签进行更改。

    这种设置无论如何都不是完美的,但肯定有一些优势。模式很简单,获取标签云很简单,通过一个简单的查询(即没有子查询)获取文章和标签。作为主要缺点,我会看到无法在不修改出现的每一行的情况下在系统范围内重命名或删除标签,但是,嘿,你多久这样做一次?

    目前我在我的几个项目中使用这个设置,它就像一个梦想一样工作,但我必须承认这些不是高流量网站(因此我逃脱了 LIKE),明年我将能够测试它与繁忙的网站,但我很确定它会做。规范化纳粹也许会投票反对我,但我只是喜欢它的简单性,我很乐意为此牺牲 cpu 周期。

    实际上,我不久前打算在 SO 上发布这个标签系统,并询问专家他们对此有何看法,所以请随时离开 cmets。

    传统上,对不起我的英语,我相信这很有趣 =)

    编辑

    由于您已在 cmets 中提供了您的要求,我认为此设置非常适合您。我已经在 pastehere 中发布了完整的标签模型,其中包含处理计数的方法,Kohana 是特定的,但如果你知道 Codeigniter,你会感到宾至如归。就这样使用吧:

    table TAGS: id, tag_name, tag_count
    
    // insert new item/article
    $tag_model->update_tags( $tags_str, null );
    
    // update existing item 
    $tag_model->update_tags( $new_tags_str, $old_tags_str ); // $old_tags as stored in db
    
    // delete item, you'll have to get item from db before deletion
    $tag_model->update_tags( null, $old_tags_str ); 
    

    我已经修改了代码,因为 markdown 已经破坏了它,查询也是 mySQL 风格,而不是 SQLite。

    【讨论】:

    • 这很好,我没有过多提及我的要求,但表格长度会低于 1000 并且每个月会有几个访问者,因为这个解决方案是针对管理员的。因此,高流量、容量、cpu 周期不会成为问题。我将不得不再读几遍才能得到它:) 并将其移至 CodeIgniter
    • 附带说明,如果您喜欢 Codeigniter,请查看 Kohana,我使用 CI 大约一年,然后搬到 Kohana,它把 CI 从水中吹走了。
    • 我 2/3 天前刚接触到 Kohana,看起来它背后有相当多的支持。但我会等到下一个项目。谢谢,感谢粘贴链接!
    【解决方案3】:

    是的,不要害怕标准化并将每个标签都放在自己的记录中。这最终将是最灵活的,并且正确的索引最快。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-01-02
      • 1970-01-01
      • 2012-06-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-12-15
      相关资源
      最近更新 更多