【问题标题】:optimizing a multi-week mysql query with php用 php 优化一个多周的 mysql 查询
【发布时间】:2013-07-14 20:48:27
【问题描述】:

我有一个脚本,它查询包含 50,000 条记录的数据库,并尝试创建一个表格,概述每周数据库中新条目总数的每周报告。

我有一个限制:报告必须从$START_DATE 开始,这是网站各部分下的数据库中存在任何用户记录的第一天。这意味着我不能使用查找预定义日期的函数,我必须以第一个用户进入数据库的第一秒为界的部分对用户进行计数,直到恰好一周后,直到 $END_DATE 通常很简单time().

在数据库中查询 count(*)select * 大约需要 5 秒:

mysql> select count(*), user_type from users_table where user_permissions = "normal" group by user_type;
+----------+-----------------+
| count(*) | user_type       |
+----------+-----------------+
|     2210 | myspace_user    | 
|    48659 | facebook_user   | 
+----------+-----------------+
2 rows in set (4.73 sec)

我有一些 PHP 代码,每个 user_type 都会遍历并查询数据库多次,以获取每周的详细报告表。问题是,有 12 周和两种用户类型,因此整个过程最多需要两分钟。网站的某些部分有两种以上的用户类型,这些查询需要更长的时间。代码如下:

$start = strtotime($START_DATE);
$end = strtotime($END_DATE);
for ($i = 0; $start+$i < time() && $start+$i < $end; $i+= (7*24*60*60)) {
    $weekly_total = 0;
    foreach($USERTYPES as $usertype) {
        $q = "select count(*) from users_table where user_type = '" . $usertype . "' and user_permissions = 'normal'";
        $q .= " and UNIX_TIMESTAMP(timestmp) >= " . strval($start+$i) . " and UNIX_TIMESTAMP(timestmp) <= " . strval($start+$i+(7*24*60*60));
        $r = mysql_query($q);
        $v = mysql_fetch_array($r);
        $table['weekly'][gmdate("Y-m-d", $start+$i)][$usertype] += $v[0];
        $weekly_total += $v[0];
    }
    $table['weekly'][gmdate("Y-m-d", $start+$i)]['weekly_total'] = $weekly_total;
}

最后我有一个包含 12 个条目的数组,其基本结构最终类似于以下伪代码:

// ...previous entries
$table['weekly']['2013-07-01'] = array(
    'myspace_user' => 123,
    'facebook_user' => 1234,
    'weekly_total' => 1357
);
$table['weekly']['2013-07-08'] = array(
// ...and so on

生成这些查询并将它们保存到表中的过程耗时过长。有什么方法可以让 MySQL 或 PHP 函数更高效,这样我就不必每周生成一个单独的查询?

【问题讨论】:

    标签: php mysql sql database optimization


    【解决方案1】:

    您的代码效率低得令人难以置信,强制​​在栅栏的两侧(PHP 和 MySQL)进行多次日期->时间戳转换。为什么不做一个更简单的

    $start = '2013-07-16';
    
    for ($week = 0; $week < 52; $week++) {
        SELECT ...
        ...
        WHERE timestmp BETWEEN ($start + INTERVAL $week WEEK)
            AND ($start + INTERVAL $week WEEK + INTERVAL 7 DAY)
    }
    

    您将坚持使用原生 MySQL 日期时间值,而无需所有重复的 native->int->native->任何转换。

    【讨论】:

    • 每个 SELECT 查询都需要 4.75 秒,与我的完全相同。计算 2 种类型 12 周的整个过程仍然需要 2 分钟。从 int 到 string 的 date->timestamp 转换花费的时间可以忽略不计,并且用于显示目的(也是因为我无法控制其中一些数据如何到达我家门口)。
    • 您还可以通过仅具有最低/最高可能日期范围并提取派生字段(例如WEEK(timestmp))将其减少为单个查询,以便您可以确定记录应该在哪个时期.
    • @joey:如果 SELECT 本身需要 4.75 秒,那么表的结构可能有问题......有多少记录? user_type 是否已编入索引? user_permissions 可能是一个字符串吗?在这里做出错误的选择会极大地影响您的查询。
    • 记录很多。这些字段都没有被索引,这是出于一个单独但合理的原因。 user_permissions 确实是一个字符串。
    猜你喜欢
    • 2013-02-17
    • 2011-10-27
    • 1970-01-01
    • 2011-12-15
    • 1970-01-01
    • 2012-09-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多