PDA

Просмотр полной версии : [Вопрос] Как получить данные (статистику по онлайну) из таблицы за 7 дней



execution
30.06.2020, 10:03
Доброе утро. Логирую отыгранное время в секундах каждый день. Хотел сделать вывод онлайна на протяжении всей недели (вывода дня недели, даты и времени), но вот сложилось сомнение а стоит ли оно? Мне необходимо будет получить кол-во секунд в каждый день недели (7 подзапросов), получения unix времени (7 подзапросов (для вывода дня недели на русском на стороне сервера)) и получения даты (7 подзапросов) - в итоге 21 подзапрос и стоит ли оно? Возможно я не в том направлении рассматриваю реализацию?

DeimoS
30.06.2020, 10:37
Доброе утро. Логирую отыгранное время в секундах каждый день. Хотел сделать вывод онлайна на протяжении всей недели (вывода дня недели, даты и времени), но вот сложилось сомнение а стоит ли оно? Мне необходимо будет получить кол-во секунд в каждый день недели (7 подзапросов), получения unix времени (7 подзапросов (для вывода дня недели на русском на стороне сервера)) и получения даты (7 подзапросов) - в итоге 21 подзапрос и стоит ли оно? Возможно я не в том направлении рассматриваю реализацию?

Создаёшь таблицу с календарём на 7 дней назад (хотя можно даже создавать на год назад и на год вперёд каждый старт сервера. Занимать это действие будет пару секунд времени). Далее пишешь один запрос, в котором получаешь дату 7 последних дней и к ней, через "LEFT JOIN", добавляешь количество отыгранных часов.

Вот, собственно, дарю готовую функцию для создания календаря:
stock CreateCalendarTable({MySQL, _}:__connection_id, days_count, const start_date[] = "CURDATE()", const direction[] = "-")
{
#define TABLE_CALENDAR_NAME_ "sys_calendar"

new tick = GetTickCount();

mysql_query(__connection_id,
"DROP TABLE IF EXISTS `"#TABLE_CALENDAR_NAME_"`", false);

mysql_query(__connection_id,
"CREATE TABLE `"#TABLE_CALENDAR_NAME_"`(`id` int(11) NOT NULL,`date` date NOT NULL) ENGINE=InnoDB DEFAULT CHARSET=utf8;", false);

mysql_query(__connection_id,
"ALTER TABLE `"#TABLE_CALENDAR_NAME_"` ADD PRIMARY KEY (`id`);", false);

mysql_query(__connection_id,
"ALTER TABLE `"#TABLE_CALENDAR_NAME_"` MODIFY `id` int(11) NOT NULL AUTO_INCREMENT;", false);

new query_string[100+11+1];
for(new i; i < days_count; i++)
{
format(query_string, sizeof(query_string), "INSERT INTO "#TABLE_CALENDAR_NAME_"(date)VALUES((%s) %s INTERVAL %d DAY)",
start_date, direction, i);
mysql_query(__connection_id, query_string, false);
}


printf(
"\
[MySQL] Календарь успешно создан. \
Название таблицы: \""#TABLE_CALENDAR_NAME_"\" \
[Количество дат: %d]. \
Времени затрачено: %d мс\
", days_count, GetTickCount()-tick);

#undef TABLE_CALENDAR_NAME_
}

В OnGameModeInit просто вставляешь
CreateCalendarTable(mysql, 366*2, "CURDATE()+INTERVAL 1 YEAR");
И функция создаст календарь, примерно, на год назад и на год вперёд, удалив старую таблицу, если она была.

Ну а запрос на получение данных пробуй сам составить. В интернете есть хорошие статьи на тему "JOIN" в MySQL.

execution
30.06.2020, 15:31
Немного не доходит, зачем использовать таблицу с календарными днями? Если можно сделать так
SELECT SUM( seconds ) AS day_online, WEEKDAY( DATE ) AS week_day, DATE
FROM `accounts_online`
WHERE account_id =118161
AND DATE > DATE_ADD( CURDATE( ) , INTERVAL -7
DAY )

Но вот пришло в голову то, чтобы считать онлайн с понедельника и мне кажется, эта таблица как раз кстати :grin:

DeimoS
30.06.2020, 16:48
Немного не доходит, зачем использовать таблицу с календарными днями? Если можно сделать так
SELECT SUM( seconds ) AS day_online, WEEKDAY( DATE ) AS week_day, DATE
FROM `accounts_online`
WHERE account_id =118161
AND DATE > DATE_ADD( CURDATE( ) , INTERVAL -7
DAY )

Но вот пришло в голову то, чтобы считать онлайн с понедельника и мне кажется, эта таблица как раз кстати :grin:

Потому что MySQL не может вернуть данные, которых нет. Следовательно, если ты не собираешься для каждого аккаунта, который не заходил на сервер, ежедневно создавать в таблице со статистикой онлайна записи, указывая 0 в качестве времени онлайна, то MySQL попросту не сможет тебе сообщить, что за такую-то дату игрок никакого времени не наиграл. А значит тебе либо придётся пропускать дни (что будет смотреться криво, как по мне), либо уже в самом моде реализовывать подбор даты для пропущенных дней (но тогда нужно учитывать и високосные года, и то, что у разных месяцев разные количества дней, и ещё некоторые моменты), либо нужно создавать временную таблицу с календарём на 7 дней каждый запрос (что неразумно), либо создавать такой календарь и отталкиваться от него, что является самым оптимальным вариантом.

- - - Добавлено - - -

Собственно, вот такой запрос будет генерировать ответ в виде статистики за последние 7 дней.
SELECT
WEEKDAY(s.date) AS week_day,
s.date,
IFNULL(SUM(a.seconds), 0) AS total
FROM
sys_calendar as s
LEFT JOIN
accounts_online as a
ON
(s.`date` = a.`DATE` AND a.account_id=118161)
WHERE
s.date >= CURDATE()-INTERVAL 7 DAY
AND
s.date <= CURDATE()
GROUP BY s.`date`
ORDER BY s.`date` DESC
И в случае, если игрок не играл в какой-то день, этот день так же будет находится в запросе. Можно даже
IFNULL(SUM(a.seconds), 0) AS total
заменить, например, на
IFNULL(SUM(a.seconds), 'Не играл') AS total
Чтоб не приходилось делать дополнительные условия, а сразу можно было получать готовый текст (правда, придётся пользоваться cache_get_field_type перед извлечением данных)

Ну и ещё я бы советовал не забывать про DATE_FORMAT (http://www.mysql.ru/docs/man/Date_and_time_functions.html), который позволяет форматировать дату ещё на этапе запроса.
Например, вот так:
DATE_FORMAT(s.`date`, '%d.%m.%Yг') as 'date',
можно на выходе сразу получить дату в формате: "30.06.2020г".

execution
04.08.2021, 10:13
Использовал такую конструкцию
SELECT \
WEEKDAY(s.date) AS week_day, \
s.date, \
IFNULL(SUM(a.seconds), 0) AS seconds_count, \
IFNULL(SUM(a.afk_seconds), 0) AS afk_seconds_count \
FROM \
sys_calendar as s \
LEFT JOIN \
accounts_online as a \
ON \
(s.`date` = a.`DATE` AND a.account_id=%d) \
WHERE \
YEARWEEK(CURDATE(), 1) = YEARWEEK(s.date, 1) \
GROUP BY s.`date` \
ORDER BY s.`date` ASC

И всё было норм, но с недавних пор, возвращает пустую строку. И как только не извивался - всё равно. Изменений не было, подобного результата другим способом нельзя сделать. На что это может повлиять, может кто подсказать?

UPD: закончился календарь :pardon:

DeimoS
05.08.2021, 16:03
UPD: закончился календарь :pardon:

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

execution
05.08.2021, 17:15
Чтоб такого не было, можешь просто каждый рестарт пересоздавать таблицу с календарём.

Да, так и сделал