[ Поиск ] - [ Пользователи ] - [ Календарь ]
Полная Версия: Оптимизация скрипта | mysql запроса
hookman
Структура таблицы:
user posted image


function cron_hour_counts()
{
$subids = get_subids();// массив вида array('1','3','10',...)
array_push($subids, '');
$from = '2011-10-20';//last_updated_date('tb_hour_counts');
$to = '2011-10-20';//last_date();
$days = days_interval($from, $to);
$result_array = array();

foreach ($subids as $subid)
{
for ($i = 0; $i < $days; $i++)
{
$hour = '00:00';
for ($t = 0; $t <= 23; $t++)
{
if ($t == 0)
{
$chour = date('H:i', strtotime($hour . '+' . $t . 'hour'));
$phour = date('H:i', strtotime('23:59'));

$date = date('Y-m-d', strtotime($from . '+' . $i . 'day'));
$date_prev = date('Y-m-d', strtotime($date . '- 1 day'));
}
else
{
$chour = date('H:i', strtotime($hour . '+' . $t . 'hour'));
$phour = date('H:i', strtotime($chour . '-1 hour'));

$date = date('Y-m-d', strtotime($from . '+' . $i . 'day'));
$date_prev = $date;
}

$unique_id_query = mysql_query("SELECT (SELECT COUNT(DISTINCT `id`,`subid`) FROM `tb_stats` WHERE (`date` < '" . mysql_real_escape_string($date) . "') OR (`date` = '" . mysql_real_escape_string($date) . "' AND `time` <= '" . mysql_real_escape_string($chour) . "')" . (!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) . "') OR (`date` = '" . mysql_real_escape_string($date_prev) . "' AND `time` <= '" . mysql_real_escape_string($phour) . "')" . (!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) . "' AND `time` <= '" . mysql_real_escape_string($chour) . "'" . (!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` < '" . mysql_real_escape_string($date) . "') OR (`date` = '" . mysql_real_escape_string($date) . "' AND `time` <= '" . mysql_real_escape_string($chour) . "')" . (!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) . "' AND `time` <= '" . mysql_real_escape_string($phour) . "'" . (!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) . "' AND `time` <= '" . mysql_real_escape_string($chour) . "'" . (!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) . "' AND `time` <= '" . mysql_real_escape_string($chour) . "'" . (!empty($subid) && is_numeric($subid) ? " AND `subid` = '" . mysql_real_escape_string($subid) . "'" : ""));
$global_result = mysql_fetch_assoc($global_query);

$result = array();
$result['date'] = $date;
$result['hour'] = $chour;
$result['subid'] = $subid;
$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
print_r($result_array);
}


unique_ids = количество уникальных значений id+subid (тех которые ранее в таблице не встречались) за каждый час где subid = ...
unique_ips = = количество уникальных значений ip+subid (тех которые ранее в таблице не встречались) за каждый час где subid = ...
total_ids = количество distinct значений id+subid за каждый час где subid = ...
total_ips = количество distinct значений ip+subid за каждый час где subid = ...
global = количество distinct значений id за каждый час где subid = ...

При необходимости будет вести подсчёт не только за 24 часа текущих суток, но и нескольких дней.

Если принять во внимание 20 subid и одни сутки то это занимает 40 минут :(



Спустя 40 минут, 44 секунды (24.11.2011 - 00:33) Placido написал(а):
На какие поля индексы ставили?



Спустя 22 минуты, 28 секунд Placido написал(а):
И, по-хорошему, неплохо было бы написать, что хотим выбрать из этой таблицы и при каких условиях.
Честно - этот код читать эпически неудобно. Если запрос(ы) SQL сложный(сложные), и его(их) нужно оптимизировать, пишите только SQL, без PHP.

Спустя 55 минут, 16 секунд (24.11.2011 - 01:28) hookman написал(а):
Цитата (Placido @ 23.11.2011 - 21:33)
На какие поля индексы ставили?

subid,date,time

нужно посчитать четыре значения unqiue_ids, unique_ips, total_ids, total_ips, global
Как они считаются я написал.
Сначала идёт цикл foreach который проходит по каждому subid, в нём for (для дней, их может быть несколько), и ещё один for для часов.

Код по-моему читабелен. Может запостить запросы в чистом виде?

Спустя 17 минут, 51 секунда (24.11.2011 - 01:46) Placido написал(а):
Поставьте составной индекс на поля date, time, subid.
CREATE INDEX `ix_all` ON `tb_stats` (`date`, `time`, `subid`);

Выборка значительно ускорится.
Просьба отписаться о результатах.



Спустя 3 минуты, 24 секунды Placido написал(а):
Поставьте составной индекс и проверьте.

А запостить запросы в чистом виде - это именно то, что я советовал. Ответов будет на порядок больше.

Спустя 3 минуты, 41 секунда (24.11.2011 - 01:49) hookman написал(а):
Цитата (Placido @ 23.11.2011 - 22:46)
Поставьте составной индекс на поля date, time, subid.
CREATE INDEX `ix_all` ON `tb_stats` (`date`, `time`, `subid`);

Выборка значительно ускорится.
Просьба отписаться о результатах.

У меня уже стоит составной индекс на эти три поля, а не по отдельности... Забыл упомянуть :)

Спустя 2 минуты, 24 секунды (24.11.2011 - 01:52) Placido написал(а):
Поля в составном индексе должны идти именно в таком порядке - (`date`, `time`, `subid`).

Спустя 30 секунд (24.11.2011 - 01:52) hookman написал(а):
Вот это unique_ids. Unique_ips идентично, только вместо id - ip.

SELECT (SELECT COUNT(DISTINCT id,subid) FROM tb_stats
WHERE subid = '1' AND (date < '2011-10-20' OR date = '2011-10-20' AND time <= '02:00')) - (SELECT COUNT(DISTINCT id,subid) FROM tb_stats
WHERE subid = '1' AND (date < '2011-10-20' OR date = '2011-10-20' AND time <= '01:00')) AS unique_ids;

Спустя 9 минут, 42 секунды (24.11.2011 - 02:02) Placido написал(а):
Цитата (hookman @ 23.11.2011 - 22:52)
Вот это unique_ids. Unique_ips идентично, только вместо id - ip.

SELECT (SELECT COUNT(DISTINCT id,subid) FROM tb_stats WHERE subid = '1' AND (date < '2011-10-20' OR date = '2011-10-20' AND time <= '02:00')) - (SELECT COUNT(DISTINCT id,subid) FROM tb_stats WHERE subid = '1' AND (date < '2011-10-20' OR date = '2011-10-20' AND time <= '01:00')) AS unique_ids;

В таком случае, составной индекс сделайте так:
CREATE INDEX `ix_all` ON `tb_stats` (`subid`, `date`, `time`).
И проверьте запрос с новым индексом.

Спустя 9 минут, 38 секунд (24.11.2011 - 02:12) hookman написал(а):
Цитата (Placido @ 23.11.2011 - 23:02)

В таком случае, составной индекс сделайте так:
CREATE INDEX `ix_all` ON `tb_stats` (`subid`, `date`, `time`).
И проверьте запрос с новым индексом.

У меня индекс именно в таком порядке subid,date,time

Спустя 13 часов, 22 минуты, 48 секунд (24.11.2011 - 15:34) Placido написал(а):
Кстати, а почему бы из трех select-ов не сделать один? Глядишь, и скорость увеличится)))

SELECT COUNT(DISTINCT id,subid) AS unique_ids FROM tb_stats 
WHERE subid = '1' AND (date < '2011-10-20' OR date = '2011-10-20' AND time BETWEEN '01:00' and '02:00');

Спустя 12 минут, 17 секунд (24.11.2011 - 15:47) hookman написал(а):
Цитата (Placido @ 24.11.2011 - 12:34)
Кстати, а почему бы из трех select-ов не сделать один? Глядишь, и скорость увеличится)))

SELECT COUNT(DISTINCT id,subid) AS unique_ids FROM tb_stats 
WHERE subid = '1' AND (date < '2011-10-20' OR date = '2011-10-20' AND time BETWEEN '01:00' and '02:00');

Этот запрос показывает количество уникальных значений за период с самой ранней даты до 2011-10-20 00:00 + с 2011-10-20 01:00 до 2011-10-20.

Мне же нужно количество значений(с 2011-10-20 01:00 до 2011-10-20 02:00) которые встречаются в таблице впервые

Спустя 49 минут, 19 секунд (24.11.2011 - 16:36) Placido написал(а):
SELECT COUNT(DISTINCT `id`) AS `unique_ids`
FROM `tb_stats`
WHERE `date` = '2011-10-20' AND `time` BETWEEN '01:00' AND '02:00'
AND `subid` = '1' AND `id` NOT IN (SELECT `id`
FROM `tb_stats`
WHERE `subid` = '1' AND `date` < '2011-10-20');

Спустя 4 часа, 34 минуты, 42 секунды (24.11.2011 - 21:11) hookman написал(а):
Цитата (Placido @ 24.11.2011 - 13:36)
SELECT COUNT(DISTINCT `id`) AS `unique_ids`
FROM `tb_stats`
WHERE `date` = '2011-10-20' AND `time` BETWEEN '01:00' AND '02:00'
AND `subid` = '1' AND `id` NOT IN (SELECT `id`
FROM `tb_stats`
WHERE `subid` = '1' AND `date` < '2011-10-20');

Если к этому добавить DISTINCT `id`,`subid` то какой синтаксис будет в NOT IN?
Сомневаюсь что этот запрос будет работать быстрее, но попробовать стоит. Спасибо.

Спустя 25 минут, 11 секунд (24.11.2011 - 21:36) Placido написал(а):
А смысл выбирать `subid`? Количество строк ведь будет то же самое, что и при выборке только `id` (`subid` у вас для всех выбранных записей будет равен 1, так как у вас есть условие WHERE `subid` = '1). А в NOT IN синтаксис тот, какой привел я.

Спустя 46 минут, 11 секунд (24.11.2011 - 22:22) hookman написал(а):
Цитата (Placido @ 24.11.2011 - 18:36)
А смысл выбирать `subid`? Количество строк ведь будет то же самое, что и при выборке только `id` (`subid` у вас для всех выбранных записей будет равен 1, так как у вас есть условие WHERE `subid` = '1). А в NOT IN синтаксис тот, какой привел я.

Не всегда $subid будет в условии WHERE, именно поэтому в запросе(смотреть скрипт) присутствует тернарный оператор.

Спустя 2 часа, 9 минут, 7 секунд (25.11.2011 - 00:31) hookman написал(а):
Этот запрос выполняется 1,2 сек:

SELECT COUNT(DISTINCT `id`,`subid`) AS `unique_ids`
FROM `tb_stats`
WHERE `date` = '2011-10-20'
AND `subid` = '5' AND CONCAT(`id`,0x3a,`subid`) NOT IN (SELECT CONCAT(`id`,0x3a,`subid`)
FROM `tb_stats`
WHERE `subid` = '5' AND `date` < '2011-10-20');


А этот 0,06 сек:

SELECT (SELECT COUNT(DISTINCT id,subid) FROM tb_stats
WHERE subid = '5' AND date <= '2011-10-20') - (SELECT COUNT(DISTINCT id,subid) FROM tb_stats
WHERE subid = '5' AND date < '2011-10-20') AS unique_ids;

Спустя 23 минуты, 51 секунда (25.11.2011 - 00:55) Placido написал(а):
Во-первых, уберите CONCATы. Зачем искать CONCAT(`id`,0x3a,`subid`), если подзапрос вернет CONCAT(`id`,0x3a,`subid`), где `subid`гарантированно будет равен 5 (у вас же и в запросе, и в подзапросе условие стоит WHERE `subid` = '5', значит CONCAT(0x3a,`subid`) всегда будет давать "0x3a5"). И у вас в первом запросе индексы не используются. Создайте составной индекс CREATE INDEX `ix_2` ON `tb_stats` (`subid`, `date`, `id`) и перепишите запрос так (для использования индексов):

SELECT COUNT(DISTINCT `id`,`subid`) AS `unique_ids`
FROM `tb_stats`
WHERE `subid` = '5'
AND `date` = '2011-10-20'
AND `id` NOT IN (SELECT `id`
FROM `tb_stats`
WHERE `subid` = '5' AND `date` < '2011-10-20');

Спустя 54 минуты, 23 секунды (25.11.2011 - 01:49) hookman написал(а):
Цитата (Placido @ 24.11.2011 - 21:55)
Во-первых, уберите CONCATы. Зачем искать CONCAT(`id`,0x3a,`subid`), если подзапрос вернет CONCAT(`id`,0x3a,`subid`), где `subid`гарантированно будет равен 5 (у вас же и в запросе, и в подзапросе условие стоит WHERE `subid` = '5', значит CONCAT(0x3a,`subid`) всегда будет давать "0x3a5"). И у вас в первом запросе индексы не используются. Создайте составной индекс CREATE INDEX `ix_2` ON `tb_stats` (`subid`, `date`, `id`) и перепишите запрос так (для использования индексов):

SELECT COUNT(DISTINCT `id`,`subid`) AS `unique_ids`
FROM `tb_stats`
WHERE `subid` = '5'
AND `date` = '2011-10-20'
AND `id` NOT IN (SELECT `id`
FROM `tb_stats`
WHERE `subid` = '5' AND `date` < '2011-10-20');

Сделал. 0,58 сек. Так что не вариант.

Спустя 1 минута, 18 секунд (25.11.2011 - 01:51) Placido написал(а):
С индексом?

Спустя 25 минут, 15 секунд (25.11.2011 - 02:16) hookman написал(а):
Да

Спустя 1 день, 19 часов, 25 минут, 3 секунды (26.11.2011 - 21:41) hookman написал(а):
Придумал решение. В результате производительность увеличилась в 20 раз.


function cron_hour_counts()
{
$subids = get_subids();
//array_push($subids, '');
$from = '2011-10-20';//last_updated_date('tb_hour_counts');
$to = '2011-10-20';//last_date();
$days = days_interval($from, $to);
$result_array = array();

for ($i = 0; $i < $days; $i++)
{
$hour = '00:00';
for ($t = 0; $t <= 23; $t++)
{
$date = date('Y-m-d', strtotime($from . '+' . $i . 'day'));
$currentHour = date('H:i', strtotime($hour . '+' . $t . 'hour'));
$nextHour = date('H:i', strtotime($currentHour . '+59 minutes'));

$unique_ids_query = mysql_query("
SELECT COUNT(id) AS unique_ids,subid
FROM
(
SELECT id,subid,date,time
FROM tb_stats
WHERE date <= '"
. mysql_real_escape_string($date) . "'
GROUP BY id,subid
) AS id_inner
WHERE date = '"
. mysql_real_escape_string($date) . "'
AND time BETWEEN '"
. mysql_real_escape_string($currentHour) . "' AND '" . mysql_real_escape_string($nextHour) . "'
GROUP BY subid;
"
);
pull_data('unique_ids', $date, $currentHour, $unique_ids_query, $subids, $result_array);

$unique_ips_query = mysql_query("
SELECT COUNT(ip) AS unique_ips,subid
FROM
(
SELECT ip,subid,date,time
FROM tb_stats
WHERE date <= '"
. mysql_real_escape_string($date) . "'
GROUP BY ip,subid
) AS ip_inner
WHERE date = '"
. mysql_real_escape_string($date) . "'
AND time BETWEEN '"
. mysql_real_escape_string($currentHour) . "' AND '" . mysql_real_escape_string($nextHour) . "'
GROUP BY subid;
"
);
pull_data('unique_ips', $date, $currentHour, $unique_ips_query, $subids, $result_array);

$total_ids_query = mysql_query("
SELECT COUNT(DISTINCT id,subid) AS total_ids,subid
FROM tb_stats
WHERE date = '"
. mysql_real_escape_string($date) . "'
AND `time` <= '"
. mysql_real_escape_string($nextHour) . "'
GROUP BY subid
"
);
pull_data('total_ids', $date, $currentHour, $total_ids_query, $subids, $result_array);

$total_ips_query = mysql_query("
SELECT COUNT(DISTINCT ip,subid) AS total_ips,subid
FROM tb_stats
WHERE date = '"
. mysql_real_escape_string($date) . "'
AND `time` <= '"
. mysql_real_escape_string($nextHour) . "'
GROUP BY subid;
"
);
pull_data('total_ips', $date, $currentHour, $total_ips_query, $subids, $result_array);

$global_query = mysql_query("
SELECT COUNT(id) AS global,subid
FROM tb_stats
WHERE date = '"
. mysql_real_escape_string($date) . "'
AND time <= '"
. mysql_real_escape_string($nextHour) . "'
GROUP BY subid;
"
);
pull_data('global', $date, $currentHour, $global_query, $subids, $result_array);
}
}


print_r($result_array);
}
Быстрый ответ:

 Графические смайлики |  Показывать подпись
Здесь расположена полная версия этой страницы.
Invision Power Board © 2001-2024 Invision Power Services, Inc.