В настоящее время я пытаюсь рассчитать число уникальных посещений пользователей в моем приложении на основе пола пользователя. Вот пример запроса, который вычисляет все посещения (не уникальные)
SELECT
DATE(v.visited_at) AS visit_date,
SUM(IF(u.gender = 'M', 1, 0)) AS male_visit,
SUM(IF(u.gender = 'F', 1, 0)) AS female_visit,
SUM(IF(u.gender = '' OR u.gender IS NULL, 1, 0)) AS unknown_visit
FROM
visits v
INNER JOIN users u ON v.user_id = u.id
WHERE
DATE(v.visited_at) >= DATE_SUB(SYSDATE(), INTERVAL 30 DAY)
AND v.duration > 30
GROUP BY
DATE(v.visited_at)
Пробовал использовать подзапрос и счетчик, он работает, но в 4 раза медленнее.
SELECT
DATE(visited_at) as visit_date,
(SELECT COUNT(DISTINCT u.id) FROM visits v JOIN users u ON v.user_id = u.id WHERE u.gender = 'M' AND DATE(v.visited_at) = visit_date AND v.duration > 30) AS male_visit,
(SELECT COUNT(DISTINCT u.id) FROM visits v JOIN users u ON v.user_id = u.id WHERE u.gender = 'F' AND DATE(v.visited_at) = visit_date AND v.duration > 30) AS female_visit,
(SELECT COUNT(DISTINCT u.id) FROM visits v JOIN users u ON v.user_id = u.id WHERE u.gender = '' OR u.gender IS NULL AND DATE(v.visited_at) = visit_date AND v.duration > 30) AS unknown_visit
FROM
visits v
WHERE
DATE(visited_at) >= DATE_SUB(SYSDATE(), INTERVAL 30 DAY)
GROUP BY
DATE(visited_at)
Любое предложение по этому поводу?
COUNT(DISTINCT)
всегда будет медленнее, чем COUNT()
. Ты можешь попробовать:
SELECT DATE(v.visited_at) AS visit_date,
COUNT(DISTINCT CASE WHEN u.gender = 'M' THEN u.id END) AS male_visit,
COUNT(DISTINCT CASE WHEN u.gender = 'F' THEN u.id END) AS female_visit,
COUNT(DISTINCT CASE WHEN u.gender = '' OR u.gender IS NULL THEN u.id END) AS unknown_visit
FROM visits v INNER JOIN
users u
ON v.user_id = u.id
WHERE DATE(v.visited_at) >= DATE_SUB(SYSDATE(), INTERVAL 30 DAY) AND
v.duration > 30
GROUP BY DATE(v.visited_at);
Однако я не знаю, будет ли это намного быстрее.
Этот тип запроса, вероятно, будет медленным, особенно если у вас есть большое количество записей в таблице, так как при выборе строк на основе значений даты и времени mysql должен выполнить полное сканирование таблицы.
Оптимизация структуры базы данных, скорее всего, принесет вам прирост производительности, намного превышающий все, что вы попробуете запросить у него, как это.
Пара предложений заключалась бы в разделении таблиц по диапазонам дат. Это может значительно сократить выполнение запросов, поскольку это означает, а не полное сканирование таблицы. Mysql может просто игнорировать любые разделы за пределами диапазона дат запроса. Чем больше таблица, тем больше пользы вы увидите, но, возможно, что-то от 2x до 10x быстрее ожидалось бы.
Если бы вы заменили свой столбец на два столбца на male
, female
и unknown
вы заменили бы 3 запроса, содержащие медленные операторы COUNT (DISTINCT... с одним запросом с меньшими условиями), вы также можете добавить идентификатор пользователя в группу чтобы удалить необходимость подсчета, поскольку вы можете указать более одного столбца для группировки.
Наконец, вы можете добавить триггер базы данных и либо добавить дополнительный столбец, который он устанавливает как 1 при регистрации посещений, если продолжительность превышает 30, и это их первое посещение дня, или вы создаете новую таблицу календаря для посещений и имеете триггер увеличивайте значение внутри этого значения при записи базы данных каждого журнала, что приравнивается к уникальному посещению за день.
Есть 2 таблицы в соответствии с запросом (пользователь и посещение) с образцами данных.
SELECT
DATE(v.visited_date) AS visit_date,
u.gender,
COUNT(DISTINCT v.user_id) AS total_count
FROM
visits v
INNER JOIN users u ON v.user_id = u.id
WHERE
DATE(v.visited_date) >= DATE_SUB(SYSDATE(), INTERVAL 30 DAY)
AND v.duration >= 30
GROUP BY u.gender,DATE(v.visited_date)
ORDER BY DATE(v.visited_date) ASC;
Этот запрос даст вам уникальное количество пользователей по гендерному признаку для конкретной даты.