通过多次迭代优化MySql查询

时间:2013-04-30 12:18:45

标签: php mysql pdo

我有一个查询,我用来构建一组数据,它应该按照每个管理员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,是hot_user) VALUES (1,NOW() - INTERVAL 10 DAY,1),(1,NOW() - INTERVAL 1 DAY,0), (1,NOW() - INTERVAL 100 DAY,1),(1,NOW() - INTERVAL 8 DAY,0),

(2,NOW() - INTERVAL 1 DAY,1),(2,NOW() - INTERVAL 100 DAY,1), (2,NOW() - INTERVAL 5 DAY,1),(2,NOW(),0),

(3,NOW(),0),(3,NOW() - INTERVAL 1 DAY,0),(3,NOW() - 100 DAY,1), (3,NOW() - INTERVAL 4 DAY,0),(3,NOW() - INTERVAL 5 DAY,0)

根据@VolkerK的要求编辑,粗体是应该由查询选择的行,前3个热门用户,在last_updated列中也具有最新值,或者只有最新用户(如果少于热门用户tan 3为此特定管理员

2 个答案:

答案 0 :(得分:0)

不。看来你已经在使用正确的方式了 虽然你也可以绑定$ 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;   
} 

让你的困惑直截了当:

  • 作为dact的问题,fetchAll没有任何魔法创造一个无处不在的阵列。它在内部执行相同的循环。所以,没有开销。
  • 无论如何都要获得运行单独查询所需的任何内容。因此,可以在循环中调整查询
  • 不信任您的感受,但衡量某些数字。如果此函数运行速度太慢而无法阻碍整个应用程序 - 请继续进行优化。如果没有 - 我还相信其他一些你可以花时间的东西,我相信

答案 1 :(得分:-2)

我不会在循环中执行这样的查询。第一种解决方案是使用… 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中的三个条目,可以实现对三个条目的限制。