SQL date_sub vs datediff 在查看日期时的表现 window
SQL date_sub vs datediff performance in looking over a date window
我正在尝试查看过去 30 天内某个产品(玩具示例)的活跃用户数。
我正在考虑两种方法。
一、date_sub
用于查找结束日期前29天(间隔为30天,包括开始日期)的日期。 where
window 然后由较早的日期和结束日期定义。
就是这个例子:
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'
我不知道哪个是更好的选择。我很乐意让其他人参与进来。
使用第一个选项:
activity_date
BETWEEN DATE_SUB(DATE("2019-07-27"), INTERVAL 29 DAY)
AND DATE("2019-07-27")
这会将存储的值直接与日期文字进行比较。这样的表达式可以利用日期列上的索引。
中,constrast 第二个表达式将日期函数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'
如果 activity_date
列上有索引,我肯定会使用第一个查询。
当您对常量值执行 DATE_SUB() 或 DATE() 时,MySQL 只需要在开始检查行之前执行该计算一次。表达式的结果是一个常量。
比较两个常量值之间的索引列,然后它可以使用该索引有效地定位匹配行,使用 range
搜索。
而如果您将您的列放在对 DATEDIFF() 的调用中,它必须重新计算检查的每一行的结果,并且它不能使用索引。它将被迫检查 table 中的每一行。这称为 table-扫描。
您可以使用 EXPLAIN 来确认这一点。第一个查询将显示 type: range 但第二个查询将显示 type: ALL,并且 row 列的 EXPLAIN 将显示大致等于 table.
大小的估计值
FWIW,这通常是正确的:将列放在函数调用中的任何表达式都会破坏该列索引的任何好处。索引之所以有效,是因为它们是按排序顺序存储的,但是 MySQL 不能在表达式或函数内的列上使用索引,因为它不会进行任何分析来确定表达式的结果是否具有与列本身相同的排序顺序。
我正在尝试查看过去 30 天内某个产品(玩具示例)的活跃用户数。
我正在考虑两种方法。
一、date_sub
用于查找结束日期前29天(间隔为30天,包括开始日期)的日期。 where
window 然后由较早的日期和结束日期定义。
就是这个例子:
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'
我不知道哪个是更好的选择。我很乐意让其他人参与进来。
使用第一个选项:
activity_date
BETWEEN DATE_SUB(DATE("2019-07-27"), INTERVAL 29 DAY)
AND DATE("2019-07-27")
这会将存储的值直接与日期文字进行比较。这样的表达式可以利用日期列上的索引。
中,constrast 第二个表达式将日期函数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'
如果 activity_date
列上有索引,我肯定会使用第一个查询。
当您对常量值执行 DATE_SUB() 或 DATE() 时,MySQL 只需要在开始检查行之前执行该计算一次。表达式的结果是一个常量。
比较两个常量值之间的索引列,然后它可以使用该索引有效地定位匹配行,使用 range
搜索。
而如果您将您的列放在对 DATEDIFF() 的调用中,它必须重新计算检查的每一行的结果,并且它不能使用索引。它将被迫检查 table 中的每一行。这称为 table-扫描。
您可以使用 EXPLAIN 来确认这一点。第一个查询将显示 type: range 但第二个查询将显示 type: ALL,并且 row 列的 EXPLAIN 将显示大致等于 table.
大小的估计值FWIW,这通常是正确的:将列放在函数调用中的任何表达式都会破坏该列索引的任何好处。索引之所以有效,是因为它们是按排序顺序存储的,但是 MySQL 不能在表达式或函数内的列上使用索引,因为它不会进行任何分析来确定表达式的结果是否具有与列本身相同的排序顺序。