Перемещение запроса MySQL из цикла - PullRequest
2 голосов
/ 22 ноября 2011

у меня есть следующий код

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 ]

0 голосов
/ 22 ноября 2011

Я бы сказал, что по крайней мере вы должны объединять запросы в цикле по одному на каждый день. Таким образом, для 5-дневного диапазона у вас будет 5 запросов.

Или вы можете иметь один запрос для всего диапазона дат и переместить его за пределы цикла (как описано в ajreal). Затем используйте PHP, чтобы разобраться во всем.

Для больших баз данных я бы предпочел немного разделить запросы, чтобы сбалансировать нагрузку и риск тайм-аутов. Также помогает поддерживать код поддерживаемым.

Вы также должны посмотреть, как ваша база данных структурирована и проиндексирована.

Заметно ли это медленно?

и необходима ли функция array_push? (не то, что это сэкономит много, просто интересно, потому что это выглядит излишним)

Если это действительно медленно, то, возможно, подумайте о полной реструктуризации процесса в зависимости от того, как вы его используете.

Вы можете, например, в 00:01 каждый день делать что-то вроде этого:

  • запросить журнал дней и подсчитать уникальную / общую сумму IP / ID
  • вставить только числа и дату в отдельную таблицу
  • архивировать дни, войти в отдельную архивную таблицу или даже в отдельную базу данных, например mongoDB

Таким образом, вы можете выполнять простые запросы для просмотра данных и манипулирования числами в соответствии с вашими предпочтениями. При архивировании вы сохраняете небольшую таблицу запросов, удаляя ненужные строки, но ведя журнал при необходимости позже.

Конечно, это может не совпадать с настройкой вашей базы данных.

0 голосов
/ 22 ноября 2011

Используйте расширение PDO :: MySQL вместо расширений MySQL или MySQLi.Таким образом, вы можете подготовить запросы, которые значительно ускорят время выполнения вызовов mysql.

0 голосов
/ 22 ноября 2011

получить все 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
0 голосов
/ 22 ноября 2011

Возьмите все свои subids и выполните выборку с предикатом IN, чтобы получить все значения одновременно.Запишите это в массив, затем зациклите массив.

...