#mysql #sql
#mysql #sql
Вопрос:
Итак, я хочу сравнить новых пользователей с возвращающимися пользователями в таблице по месяцам. У меня есть таблица, которая содержит каждое действие с именем пользователя и отметкой даты.
Я могу легко вытащить пользователей, которые выполнили действие, например, в январе 2011 года. Чтобы увидеть, является ли каждый пользователь новым, мне нужно затем запустить их имя пользователя против всех предыдущих записей (до января 2011 года).
В моих попытках я придумал следующее:
SELECT ini.username,
MIN(ini.datetime) AS firstAction,
COUNT(ini.datetime) AS numMonth,
(SELECT COUNT(*)
FROM tableActions tot
WHERE tot.username = ini.username
AND tot.datetime < '201101%'
AND tot.datetime > '201001%') AS numTotal
FROM tableActions ini
WHERE DATETIME >= '201101%'
AND DATETIME < '201102%'
GROUP BY ini.username
ORDER BY firstAction
Это не ошибка, но и не завершается. Кажется, довольно интенсивным.
Комментарии:
1. В чем ваш вопрос? Как это «исправить»? Укажите ваши требования.
2. Тип данных
datetime
столбца …varchar
? плохая идея. Ваш запрос должен быть медленным. Если тип данных столбца datetimedatetime
равен, то я не понимаю, что это за сравнение>= '201101%'
?3. Согласен, что такое объявление столбца для «datetime»?
4. Столбец datetime — varchar , я могу это исправить, спасибо, что указали на это.
5. @Tomalakgeret’kal вопросы в том, как можно сначала запросить все действия в январе 2011 года, а затем сравнить это число со всеми предыдущими действиями. По сути, я ищу новых пользователей, выполняющих действия в январе, устраняя всех, кто ранее выполнял действие. Надеюсь, так будет лучше.
Ответ №1:
Вы можете переписать запрос, который будет (при условии, что tableactions.datetime
это DATETIME
тип данных):
SELECT ini.username,
MIN(ini.datetime) AS firstAction,
COUNT(ini.datetime) AS numMonth,
x.numTotal
FROM tableActions ini
LEFT JOIN (SELECT tot.username,
COUNT(*) AS numTotal
FROM tableActions tot
WHERE tot.datetime > '2010-01-01'
AND tot.datetime < '2011-01-01'
GROUP BY tot.username) x ON x.username = ini.username
WHERE ini.datetime BETWEEN '2011-01-01' AND '2011-01-31'
GROUP BY ini.username
ORDER BY firstAction
Может помочь включить индекс username
как минимум, хотя стоит рассмотреть использование покрывающего индекса username
datetime
.
datetime
Сравнение выглядит подозрительно — LIKE
единственное, что поддерживает подстановочные знаки.
Комментарии:
1. была такая же мысль — количество (дата-время)?
2. datetime — это varchar . Должен ли я сначала исправить это, а затем попытаться выполнить запрос? Спасибо.
3. Не могу сказать, будет ли запрос работать как есть, но идея правильная. Кстати, вы можете использовать
COUNT(1)
вместоCOUNT(*)
.4. @Tim Cutting: Да — нетривиально что-то менять, когда у вас есть данные, но правильный ввод данных упростит поиск / улучшит производительность.
Ответ №2:
Я думаю, что простого соединения таблицы с самим собой с подходящим предложением where будет достаточно (этот запрос прямо из моей головы, не проверен):
SELECT curr_activity.username, COUNT(prev_activity.username) AS did_something_in_the_past
FROM tableActions AS curr_activity
LEFT JOIN tableActions AS prev_activity ON curr_activity.username = prev_activity.username
WHERE curr_activity.datetime >= '2011-01-01' AND curr_activity.datetime < '2011-02-01'
AND prev_activity.datetime < '2011-01-01'
GROUP BY curr_activity.username
Индексы имеют значение. Вы должны проиндексировать столбец username
and datetime
, а datetime
столбец должен быть a datetime
или аналогичным типом данных.
Ответ №3:
SELECT username,
MIN(datetime) AS firstAction,
MAX(datetime) AS numMonth,
COUNT(*) AS numTotal
GROUP BY ini.username
HAVING numTotal > 1
WHERE DATETIME between '201001%' AND '201102%'
ORDER BY username
* I think this collapsed version is what you need ?
Комментарии:
1. Существуют разные критерии даты — может быть опечатка, в противном случае я согласен
Ответ №4:
Я думаю, вы можете заменить
SELECT COUNT(*)
FROM tableActions tot
WHERE tot.username = ini.username
AND tot.datetime < '201101%'
AND tot.datetime > '201001%'
с
SELECT 1
FROM tableActions tot
WHERE tot.username = ini.username
AND tot.datetime < '201101%'
AND tot.datetime > '201001%' LIMIT 1
, поэтому ему не нужно перебирать все записи и подсчитывать их.
Комментарии:
1. Да, TOP предназначен для SQL Server, поэтому вместо этого вам нужно использовать
LIMIT 1
в конце подзапрос. Я не большой эксперт в MySQL, поэтому просто предположим, что это сработает.