繁体   English   中英

通过多次迭代优化MySql查询

[英]Optimizing a MySql query with multiple iterations

我有一个用于构造一组数据的查询,该数据应该按每个管理员ID的评分准确包含前3名用户。 现在,由于我不知道如何使用SQL来实现此目的,因此我要分别获取每个管理员的主要用户,然后将其推送到一个数组中。 而且,由于调用sth-> fetchAll(),然后调用array_merge(),将导致在第二次迭代及以后的迭代中有重复的数组键,从而导致致命的错误,因此我在内部还有一个内部迭代(循环)第一个,它从结果集中获取每一行,并将其推入到数组中,在那里我保存了格式化的结果。 在我的拙见中,这会导致n * 3次迭代,即n * 3 -1太多。

另外,一个BTW问题现在困扰了我好一阵子了:难道没有办法将参数或值绑定到SQL语言组件(例如LIMIT)和禁用PDO模拟的准备好的语句吗? 码:

private function getHotUsers($admins, $count = 3)
    {
        try{
            $conn = DBLink::getInstance();
            $rows = array();
            $sql = "SELECT user_name, user_id, user_group_id FROM users
            WHERE admin_id= :uid  AND status=1 ORDER BY is_hot_user DESC,last_updated DESC LIMIT {$count}";
            $sth = $conn->prepare($sql);
        foreach ($admins as $admin)
        {
            $sth->bindParam(':uid', $admin, PDO::PARAM_INT);
            $sth->execute();
            while($row = $sth->fetch(PDO::FETCH_ASSOC)){
                $rows[] = $row;
            }
        }
        return $rows;   
        }
}

| Field                | Type             | Null | Key | Default | Extra          |
+----------------------+------------------+------+-----+---------+----------------+
| user_id               | int(10) unsigned | NO   | PRI | NULL    | auto_increment |
| admin_id              | int(20)          | NO   |     | NULL    |                |
| user_title            | varchar(450)     | NO   |     | NULL    |                |
| user_desc             | varchar(5000)    | NO   |     | NULL    |                |
| user_data             | longtext         | NO   |     | NULL    |                |
| user_requirements     | varchar(5000)    | YES  |     | NULL    |                |
| user_experience       | varchar(100)     | NO   |     | NULL    |                |
| location_id           | int(11) unsigned | NO   |     | NULL    |                |
| comp_id               | int(11)          | NO   |     | NULL    |                |
| role_id               | int(10) unsigned | NO   |     | NULL    |                |
| user_pass_time        | varchar(100)     | YES  |     | NULL    |                |
| last_updated          | datetime         | NO   |     | NULL    |                |
| is_hot_user           | tinyint(1)       | NO   |     | 0       |                |
| user_internal_id      | int(10)          | YES  |     | NULL    |                |
+----------------------+------------------+------+-----+---------+----------------+

INSERT INTO USERS(admin_id,last_updated,is hot_user)值(1,NOW()-间隔10天,1),(1,NOW()-间隔1天,0),(1,NOW()-间隔100天,1) ,(1,NOW()-间隔8天,0),

(2,NOW()-间隔1天,1),(2,NOW()-间隔100天,1),(2,NOW()-间隔5天,1), (2,NOW(),0 ),

(3,NOW(),0),(3,NOW()-间隔1天,0),(3,NOW()-100天,1), (3,NOW()-间隔4天,0) ,(3,NOW()-间隔5天,0)

按照@VolkerK的要求进行编辑,以粗体显示查询应选择的行,前3个热用户,在last_updated列中也具有最新值的行;如果热用户较少,则仅是最新的用户此特定管理员的棕褐色3

不。 看来您已经在使用正确的方法。
虽然您也可以绑定$ count。 另外,尽管调用$ sth-> fetchAll()然后再调用array_merge() 不会导致键重复(请注意,这是不可能的),但我还是不会将所有用户合并到单个数组中,而是将它们合并他们由他们的管理员

private function getHotUsers($admins, $count = 3)
{
    $conn = DBLink::getInstance();
    $rows = array();
    $sql = "SELECT user_name, user_id, user_group_id FROM topUsers
            WHERE admin_id= :uid  AND status=1 
            ORDER BY is_hot_user DESC,last_updated DESC 
            LIMIT :coint";
    $sth = $conn->prepare($sql);
    foreach ($admins as $admin)
    {
        $sth->bindParam(':uid',   $admin, PDO::PARAM_INT);
        $sth->bindParam(':count', $count, PDO::PARAM_INT);
        $sth->execute();
        $rows[$admin] = $sth->fetchAll();
    }
    return $rows;   
} 

为了使您的困惑变得直截了当:

  • 顺带一提,fetchAll毫无用处创建数组。 它在内部执行相同的循环。 因此,没有任何开销。
  • 要获得前三名,您仍然需要运行单独的查询。 因此,可以循环调整查询
  • 不要相信你的感受,而是要测量某些数字 如果此功能运行太慢而阻碍了整个应用程序-请继续进行优化。 如果没有的话,我相信您还可以花一些时间做些事情

我不会在循环内执行这样的查询。 第一种解决方案是使用… IN (…)搜索ID列表(如Rob所建议)。

或者,您只需在循环中设置查询和参数,然后在循环后执行语句即可。

$start = 0;
$limit = 100;
$placeholderArray = $valuesArray = array( );
$i = 0;
foreach( $admins as $adminId )
{
    $placeholderArray[] = sprintf( ' `admin_id` = :admin%d ', $i );
    $valuesArray[sprintf( ':admin%d', $i )] = (int) $adminId;
    $i++;
}

$where = ' (' . implode( ' OR ', $placeholderArray ) . ') ';
$sql = sprintf( 'SELECT user_name, user_id, user_group_id
    FROM topUsers
    WHERE status=1
    %s
    ORDER BY is_hot_user DESC,last_updated DESC
    LIMIT %d,%d;', $where, $start, $limit );

$stmt = $conn->prepare( $sql );
if( $stmt->execute( $valuesArray ) === true )
{
    /* … */
}

通过将原始输入限制为数组$admins中的三个条目,可以实现对三个条目的限制。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM