将mysql查询移出循环

时间:2011-11-22 13:58:01

标签: php mysql high-load

我有以下代码

function cron_day_counts()
{
    $subids = get_subids();
    array_push($subids, '');
    $from = '2011-10-19';
    $to = '2011-10-20';
    $days = days_interval($from, $to);
    $result_array = array();
    foreach ($subids as $subid)
    {
        for ($i = 0; $i < $days; $i++)
        {
            $date = date('Y-m-d', strtotime($from . '+ ' . $i . ' day'));
            $date_prev = date('Y-m-d', strtotime($date . '- 1 day'));

            $unique_id_query = mysql_query('SELECT (SELECT COUNT(DISTINCT `id`,`subid`) FROM `tb_stats` WHERE `date` <= \'' . $date . '\'' . (!empty($subid) && is_numeric($subid) ? ' AND `subid` = \'' . mysql_real_escape_string($subid) . '\'' : '') . ') - (SELECT COUNT(DISTINCT `id`,`subid`) FROM `tb_stats` WHERE `date` <= \'' . mysql_real_escape_string($date_prev) . '\'' . (!empty($subid) && is_numeric($subid) ? ' AND `subid` = \'' . mysql_real_escape_string($subid) . '\'' : '') . ') AS `unique_ids`');
            $unique_id_result = mysql_fetch_assoc($unique_id_query);

            $total_id_query = mysql_query('SELECT COUNT(DISTINCT `id`,`subid`) AS `total_ids` FROM `tb_stats` WHERE `date` = \'' . mysql_real_escape_string($date) . '\'' . (!empty($subid) && is_numeric($subid) ? ' AND `subid` = \'' . mysql_real_escape_string($subid) . '\'' : ''));
            $total_id_result = mysql_fetch_assoc($total_id_query);

            $unique_ip_query = mysql_query('SELECT (SELECT COUNT(DISTINCT `ip`,`subid`) FROM `tb_stats` WHERE `date` <= \'' . $date . '\'' . (!empty($subid) && is_numeric($subid) ? ' AND `subid` = \'' . mysql_real_escape_string($subid) . '\'' : '') . ') - (SELECT COUNT(DISTINCT `ip`,`subid`) FROM `tb_stats` WHERE `date` <= \'' . mysql_real_escape_string($date_prev) . '\'' . (!empty($subid) && is_numeric($subid) ? ' AND `subid` = \'' . mysql_real_escape_string($subid) . '\'' : '') . ') AS `unique_ips`');
            $unique_ip_result = mysql_fetch_assoc($unique_ip_query);

            $total_ip_query = mysql_query('SELECT COUNT(DISTINCT `ip`,`subid`) AS `total_ips` FROM `tb_stats` WHERE `date` = \'' . mysql_real_escape_string($date) . '\'' . (!empty($subid) && is_numeric($subid) ? ' AND `subid` = \'' . mysql_real_escape_string($subid) . '\'' : ''));
            $total_ip_result = mysql_fetch_assoc($total_ip_query);

            $global_query = mysql_query('SELECT COUNT(`id`) AS `global` FROM `tb_stats` WHERE `date` = \'' . mysql_real_escape_string($date) . '\'' . (!empty($subid) && is_numeric($subid) ? ' AND `subid` = \'' . mysql_real_escape_string($subid) . '\'' : ''));
            $global_result = mysql_fetch_assoc($global_query);

            $result = array();
            $result['subid'] = $subid;
            $result['date'] = $date;
            $result['unique_ids'] = $unique_id_result['unique_ids'];
            $result['total_ids'] = $total_id_result['total_ids'];
            $result['unique_ips'] = $unique_ip_result['unique_ips'];
            $result['total_ips'] = $total_ip_result['total_ips'];
            $result['global'] = $global_result['global'];

            $result_array[] = $result;
        }

    }
    //db insert
    return $result_array;
}

我想将所有查询移出foreach和for循环,我相信它会更快。我对此感到困惑,不知道该怎么做。任何帮助将不胜感激。

4 个答案:

答案 0 :(得分:0)

获取所有subid

每张桌子的


构建单个查询以在最小日期和最大日期之间进行过滤 按日期分组

select subid, `date`, count(*) ... 
where subid IN($subids) and `date` between $smallest and $largest
group by subid, `date`

迭代结果,并将结果存储到数组中,使用subid,date作为键

$mysql_results = array[$subid][$date] ...

最后,迭代$ subids和date,比如

foreach ($subids as $subid)
{
  for ($i = 0; $i < $days; $i++)
  {
     // set $date

     // check $mysql_results[$subid][$date] exists
  }
}

如上所述,您只需要5个查询而不是

5 x total days x size of the subids

答案 1 :(得分:0)

我想至少你应该把循环中的查询组合成每天一个。因此,对于5天的范围,您将有5个查询。

或者您可以对整个日期范围进行单个查询并将其移出循环(如ajreal所述)。然后使用PHP对其进行排序。

对于大型数据库,我宁愿稍微拆分查询以平衡负载和超时风险。还有助于保持代码的可维护性。

您还应该了解数据库的结构和索引方式。

明显慢吗?

并且是必需的array_push函数吗? (并不是说会节省很多,只是想知道它看起来多余)

如果它真的很慢,那么可以考虑根据你的使用方式完全重构这个过程。

例如,您可以在每天00:01执行以下操作:

  • 查询天数日志并计算唯一/总IP / ID金额
  • 只将计数数字和日期插入单独的表格
  • 将日期归档到单独的归档表或甚至是单独的数据库,如mongoDB

通过这种方式,您可以执行简单的查询来查看数据并以良好的性能操作数字到您的内容。通过归档,您可以通过删除不必要的行来保持查询表较小,但如果稍后需要则保留日志。

当然,这可能与您的数据库设置方式不符。

答案 2 :(得分:-1)

获取所有subids,并使用IN谓词进行抓取,以便立即获取所有值。把它放到数组中,然后循环数组。

答案 3 :(得分:-1)

使用PDO :: MySQL扩展而不是MySQL或MySQLi扩展。这样,您就可以准备查询,这将大大加快mysql调用的执行时间。