[英]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;
}
为了使您的困惑变得直截了当:
我不会在循环内执行这样的查询。 第一种解决方案是使用… 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.