MySQL, DISTINCT в операции SUM

0

В настоящее время я пытаюсь рассчитать число уникальных посещений пользователей в моем приложении на основе пола пользователя. Вот пример запроса, который вычисляет все посещения (не уникальные)

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)

Любое предложение по этому поводу?

Теги:
sum

3 ответа

1
Лучший ответ

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);

Однако я не знаю, будет ли это намного быстрее.

  • 0
    это работает, только немного медленнее, но, как вы сказали, отдельная операция будет медленнее, спасибо
0

Этот тип запроса, вероятно, будет медленным, особенно если у вас есть большое количество записей в таблице, так как при выборе строк на основе значений даты и времени mysql должен выполнить полное сканирование таблицы.

Оптимизация структуры базы данных, скорее всего, принесет вам прирост производительности, намного превышающий все, что вы попробуете запросить у него, как это.

Пара предложений заключалась бы в разделении таблиц по диапазонам дат. Это может значительно сократить выполнение запросов, поскольку это означает, а не полное сканирование таблицы. Mysql может просто игнорировать любые разделы за пределами диапазона дат запроса. Чем больше таблица, тем больше пользы вы увидите, но, возможно, что-то от 2x до 10x быстрее ожидалось бы.

Если бы вы заменили свой столбец на два столбца на male, female и unknown вы заменили бы 3 запроса, содержащие медленные операторы COUNT (DISTINCT... с одним запросом с меньшими условиями), вы также можете добавить идентификатор пользователя в группу чтобы удалить необходимость подсчета, поскольку вы можете указать более одного столбца для группировки.

Наконец, вы можете добавить триггер базы данных и либо добавить дополнительный столбец, который он устанавливает как 1 при регистрации посещений, если продолжительность превышает 30, и это их первое посещение дня, или вы создаете новую таблицу календаря для посещений и имеете триггер увеличивайте значение внутри этого значения при записи базы данных каждого журнала, что приравнивается к уникальному посещению за день.

0

Есть 2 таблицы в соответствии с запросом (пользователь и посещение) с образцами данных.

Изображение 174551

Изображение 174551

запрос

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;

Изображение 174551

Этот запрос даст вам уникальное количество пользователей по гендерному признаку для конкретной даты.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню