SQL date_sub против производительности datediff при просмотре окна даты - PullRequest
0 голосов
/ 07 марта 2020

Я пытаюсь посмотреть количество активных пользователей продукта (например, игрушек) за последние 30 дней.

Я рассматриваю два подхода.

Один, date_sub используется для нахождения даты за 29 дней до (интервал составляет 30 дней, включая дату начала) даты окончания. Окно where затем определяется этой более ранней датой и датой окончания.

Вот этот пример:

SELECT 
    activity_date AS day, 
    COUNT(DISTINCT user_id) AS active_users
FROM Activity
WHERE 
    activity_date >= DATE_SUB("2019-07-27", INTERVAL 29 DAY) 
      AND 
    activity_date >= "2019-07-27" 

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

SELECT
    activity_date as day,
    COUNT(DISTINCT user_id) AS active_users
FROM Activity
WHERE 
    datediff('2019-07-27', activity_date) < 30
      AND
    activity_date <= '2019-07-27'

Я не понимаю, какой вариант лучше. Я бы хотел, чтобы другие взвесили.

Ответы [ 2 ]

2 голосов
/ 07 марта 2020

Используйте первую опцию:

activity_date 
    BETWEEN DATE_SUB(DATE("2019-07-27"), INTERVAL 29 DAY) 
    AND DATE("2019-07-27") 

Сравнивает сохраненное значение непосредственно с датой литералов. Такое выражение может использовать индекс для столбца даты.

В отличие от этого, второе выражение применяет функцию даты datediff() к столбцу даты. Это делает выражение не SARGable, что означает, что оно не принесет пользы для индекса:

datediff('2019-07-27', activity_date) < 30
and activity_date <= '2019-07-27'

Обратите внимание, что первое выражение может быть просто сформулировано:

activity_date >= '2019-07-27' - interval 29 day
and activity_date <= '2019-07-27'

Я не уверен, что второе сравнение должно быть >=, а не >. Причина, по которой это имеет смысл, заключается в том, что activitydate не имеет временной составляющей. Но я бы рекомендовал использовать <, потому что он работает в обоих случаях; если вам нужны данные до '2019-07-27', вы можете сделать:

activity_date >= '2019-07-27' - interval 29 day
and activity_date < '2019-07-28'
1 голос
/ 07 марта 2020

Я бы определенно использовал первый запрос, если у вас есть индекс для столбца activity_date.

Когда вы выполняете DATE_SUB () или DATE () для константных значений, MySQL нужно только сделать этот расчет один раз, прежде чем он начинает изучать строки. Результатом выражения является константа.

Сравнение индексированного столбца МЕЖДУ двумя константными значениями, затем он может использовать этот индекс для эффективного поиска совпадающих строк, используя поиск range.

Принимая во внимание, что если вы помещаете свой столбец в вызов DATEDIFF (), он должен пересчитать результат в каждой проверенной строке и не может использовать индекс. Он будет вынужден изучить каждую строку в таблице. Это называется сканированием таблицы.

Вы можете использовать EXPLAIN для подтверждения этого. Первый запрос покажет type: range , но второй запрос покажет type: ALL , а столбец row в EXPLAIN покажет оценку, примерно равную размер таблицы.

FWIW, это, как правило, верно: любое выражение, в которое вы помещаете столбец внутри вызова функции, портит любое преимущество индекса для этого столбца. Индексы работают, потому что они хранятся в отсортированном порядке, но MySQL не может использовать индекс для столбца внутри выражения или функции, потому что он не выполняет никакого анализа, чтобы определить, имеет ли результат выражения тот же вид порядок как сам столбец.

...