Перемещение запроса MySQL из цикла
У меня есть следующий код
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, и я считаю, что он будет работать быстрее. Я застрял, не зная, как это сделать. Любая помощь будет оценена.
4 ответа
Я бы сказал, что по крайней мере вы должны объединять запросы в цикле по одному на каждый день. Таким образом, для 5-дневного диапазона у вас будет 5 запросов.
Или вы можете иметь один запрос для всего диапазона дат и переместить его за пределы цикла (как описано в ajreal). Затем используйте PHP, чтобы разобраться во всем.
Для больших баз данных я бы предпочел немного разделить запросы, чтобы сбалансировать нагрузку и риск тайм-аутов. Также помогает сохранить код поддерживаемым.
Вы также должны посмотреть, как ваша база данных структурирована и проиндексирована.
Заметно ли это медленно?
и нужна ли функция array_push? (не то, что это сэкономит много, просто интересно, потому что это выглядит излишним)
Если это действительно медленно, то, возможно, подумайте о полной реструктуризации процесса в зависимости от того, как вы его используете.
Вы можете, например, в 00:01 каждый день делать что-то вроде этого:
- запросить журнал дней и подсчитать уникальные / общие суммы IP/ID
- вставьте только цифры и дату в отдельную таблицу
- архивировать дни, войти в отдельную архивную таблицу или даже отдельную базу данных, например, mongoDB
Таким образом, вы можете выполнять простые запросы для просмотра данных и манипулирования числами в соответствии с вашими предпочтениями. При архивировании вы сохраняете небольшую таблицу запросов, удаляя ненужные строки, но при необходимости ведя журнал.
Конечно, это может не соответствовать тому, как настроена ваша БД.
Получить все 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 и дату, как
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
Возьми все свои subids
и сделать выборку с IN
Предикат, чтобы получить все значения одновременно. Наполните это массивом, затем зациклите массив.
Используйте расширение PDO::MySQL вместо расширений MySQL или MySQLi. Таким образом, вы можете подготовить запросы, которые значительно ускорят время выполнения вызовов mysql.