使用多个迭代优化MySql查询


Optimizing a MySql query with multiple iterations

我有一个查询,我用它来构造一组数据,它应该包含每个管理员ID评级的前3个用户。现在,因为我不知道如何使用SQL实现这一点,所以我分别为每个管理员获取顶级用户,然后将它们推入一个数组。此外,由于调用sth->fetchAll(),然后调用array_merge(),将导致在第二次迭代和以后的迭代中有重复的数组键,因此将导致致命错误,因此我还在第一次迭代中有一个内部迭代(循环),它从结果集中获取每一行,并将其推入我保存格式化结果的数组中。这会导致n *3次迭代,在我看来,这是n *3 -1太多了。

还有,一个问题已经困扰我很长一段时间了:是否没有办法将参数或值绑定到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 () - interval 10天,1),(1,now () - interval 1天,0),(1,现在()——间隔100天,1),(1,现在()——间隔8天,0),

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

(3,现在(),0)、(3)、现在()——间隔1天,0)、(3)、现在()- 100天,1),(3, NOW() - INTERVAL 4 DAY, 0), (3, NOW() - INTERVAL 5 DAY, 0)

根据@VolkerK的请求编辑,粗体是查询应该选择的行,前3个热门用户,他们的last_updated列中也有最近的值,或者只是最新的用户,如果这个特定的admin的热门用户少于3个

没有。看来你的方法已经正确了。
不过您也可以绑定$count。此外,虽然调用$sth->fetchAll(),然后调用array_merge(),将导致有重复的键(这是不可能的,请注意),我不会合并所有的用户到一个数组,无论如何,而是按他们的admin分组他们

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不会凭空创建一个数组。它在内部做同样的循环。所以,没有开销。
  • 获得任何你需要运行单独查询的前3名。因此,可以在循环中打开查询
  • 不相信你的感觉但测量某些数字。如果这个函数运行得很慢,妨碍了整个应用程序,那么继续优化吧。如果没有,我相信你可以花时间做一些其他的事情。

我不会在循环中执行这样的查询。第一个解决方案是使用… 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中的原始输入限制为三个条目来实现的。