高效无重复的获取随机文章

前言

之前跟别人定制模板的时候也用到过调用随机文章的,不过之前都是随机取200篇文章再做随机数据,这样的缺点就是一些老的文章永久都获取不到。

其他开发者也有很多获取随机数的代码,一般以下两种:

1、用rand()这样的sql查询,这个有个致命的缺点,就是如果网站文章数据量很大的话,会很慢,文章再多的话php就报内存溢出了。

2、用随机数范围,取出文章最大ID和最小ID,生成之间的随机数,利用这些随机数去取文章。但是这样有一个缺点,假如中间有文章被删除了,这个ID的文章就会不存在,这样获取的文章就会不足数。

以下代码也是利用上面第二种的方法加以改进,缺少的文章再补上

代码

代码我写了两种,推荐用第一个。

function GetRandArticles($count , $where = '' , $real = ''){
    global $zbp;
    
    $where = $where?:array(array('=', 'log_Type', '0') , array('=' , 'log_Status' , '0'));
    
    //判断一下网站文章总数是不是少于需要的随机数量,少就直接获取文章并返回
    if (GetValueInArrayByCurrent($zbp->db->Query($zbp->db->sql->Count($zbp->db->dbpre.'post', array(array('COUNT', '*', 'num')), $where)), 'num') <= $count) return $zbp->GetArticleList('*',$where);
    
    //固定一下要调用的文章数量,如果第一次调用后的数量不够数的话,还得再次调用自身函数,得需要这个值
    if (!$real) $real = $count;
    
    //定义全局变量,因为可能会需要调用多次本函数,所以得把获得得文章存在全局变量中
    if (!isset($GLOBALS['articles'])) $GLOBALS['articles'] = array();
    
    //定义全局变量,同上。将已获得得文章ID存此变量
    if (!isset($GLOBALS['randid'])) $GLOBALS['randid'] = array();

    //获取所有文章最大ID和最小ID
    $sql = $zbp->db->sql->Select($zbp->table['Post'] , "MIN(log_ID),MAX(log_ID)" , $where);
    $res = $zbp->db->Query($sql);
    $limits = array_values($res[0]);
    
    //生成上面最大ID和最小ID之间随机数
    $ids = $or = array();
    for ($i = 0; $i < $count; $i++) {
        $ids[] = mt_rand($limits[0],$limits[1]);
    }
    
    //拼接sql or 语句。
    foreach ($ids as $id) {
        //检查一下获得的文章ID有没有在之前就获取到过,防止有重复的ID
        if (array_search($id,$GLOBALS['randid']) !== false) continue;
        $or[] = array('log_ID',$id);
        $GLOBALS['randid'][] = $id;
    }
    
    //也可能本次获取的随机数都是和之前获取的全部重复了,所以得判断一下
    if ($or) {
        $w = $where;
        $ors = array('array',$or);
        $w[] = $ors;
        //获取文章
        $res = $zbp->GetArticleList('*' , $w);
        //将获得得文章存在全局变量中
        $GLOBALS['articles'] = array_merge($GLOBALS['articles'],$res);
    }

    //计算一下,本次获取的文章数量有没有够数。
    if (($count = $real - count($GLOBALS['articles'])) > 0) {
        //不够数继续调用自身函数再次获取
        return GetRandArticles($count , $where , $real);
    }else{
        //够数,返回文章对象数组
        return $GLOBALS['articles'];
    }
}

第二种代码:

这是第一次的时候写的,但是发现这样对数据库的查询次数比较多,所以改进最终得到上面的方法。

function GetRandArticles($count){
    global $zbp;
    
    //获取目前文章的最大ID和最小ID,方便取随机数
    $sql = $zbp->db->sql->Select($zbp->table['Post'],"MIN(log_ID),MAX(log_ID)" , array(array('=' , 'log_Type' , '0') , array('=' , 'log_Status' , '0')));
    $res = $zbp->db->Query($sql);
    $limits = array_values($res[0]);
    
    $articles = array();
    $ids = array();
    do {
        //获得一个随机数
        $id = mt_rand($limits[0],$limits[1]);
        
        //检查一下之前有没有已经获得过这个ID,如果有则跳过当次循环
        if (in_array($id,$ids)) continue;
        
        //获得文章对象
        $post = GetPost((int)$id);
        
        //判断文章是否存在
        if ($post->ID){
            $ids[] = $post->ID;
            $articles[] = $post;
        }
        
        //判断是否够数,够数则种植do循环,不够则继续循环获取
        if (count($ids) == $count){
            $goto = false;
        }else{
            $goto = true;
        }
    } while($goto);
    return $articles;
}

调用方法

//参数1:文章数量
//参数2:$where 数组 (可选,不推荐使用)

foreach (GetRandArticles(10) as $article) {
    echo $article->Title;
}

//比如获取分类ID为21下的随机10篇文章,这个方法尽量不要使用
GetRandArticles(10,array(array('=', 'log_Type', '0') , array('=' , 'log_Status' , '0'),array('=' , 'log_CateID' , 21)))

第二种方法不支持第二参数

提醒:如果网站中间断篇的文章越多,查询数据库次数耗时更长。

zblog 

相关文章

评论:

2 条评论

发表评论 取消回复

很抱歉,您暂时无法发布评论。需要 登录 后才能发布。