SQL 在更大数据上为每个用户选择 first/last 行时的性能 table
SQL Performance on selecting first/last row for each user on bigger data table
我已经阅读了很多关于 greatest-n-per-group 的帖子,但似乎仍然没有在性能方面找到好的解决方案。我是 运行 10.1.43-MariaDB。
我正在尝试获取给定时间范围内数据值的变化,因此我需要从该时间段获取最早和最新的行。现在需要计算的时间范围内的最大行数约为 700k,并且只会增加。现在我刚刚做了两个查询,一个是最近的,一个是最早的,但即使这样,目前的性能也很慢。 table 看起来像这样:
user_id data date
4567 109 28/06/2019 11:04:45
4252 309 18/06/2019 11:04:45
4567 77 18/02/2019 11:04:45
7893 1123 22/06/2019 11:04:45
4252 303 11/06/2019 11:04:45
4252 317 19/06/2019 11:04:45
日期和 user_id 列已编入索引。没有排序,如果有区别的话,行在数据库中没有任何特定的顺序。
我在这个问题上得到的最深入的查询是针对当前年份的查询(700k 数据点):
SELECT user_id,
MIN(date) as date, data
FROM datapoint_table
WHERE date >= '2019-01-14'
GROUP BY user_id
这给了我正确的日期和 user_id 在大约 ~0.05 秒内非常快。但是就像每组最大 n 的常见问题一样,该行的其余部分(在本例中为数据)与日期不在同一行。我已经阅读了其他类似的问题并尝试使用这样的子查询:
SELECT a.user_id, a.date, a.data
FROM datapoint_table a
INNER JOIN (
SELECT datapoint_table.user_id,
MIN(date) as date, data
FROM datapoint_table
WHERE date >= '2019-01-01'
GROUP BY user_id
) b ON a.user_id = b.user_id AND a.date = b.date
此查询大约需要 15 秒才能完成并获取正确的数据值。 15 秒太长了,当第一个查询如此之快时,我一定是做错了什么。我还尝试对 user_id 分组的数据进行 (MAX)-(MIN),但它的性能也很慢。
要获得与日期相同的数据值,甚至每个用户的最新和最早数据的差异,什么是更有效的方法?
假设您使用的是 MariaDB 或 MySQL 的最新版本,那么 ROW_NUMBER
可能是为每个用户查找最早记录的最有效方法:
WITH cte AS (
SELECT *, ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY date) rn
FROM datapoint_table
WHERE date > '2019-01-14'
)
SELECT user_id, data, date
FROM cte
WHERE rn = 1;
在上面还可以考虑增加如下索引:
CREATE INDEX ON datapoint_table (user_id, date);
您还可以尝试使用以下列颠倒的变体索引:
CREATE INDEX ON datapoint_table (date, user_id);
尚不清楚哪个版本的索引性能最好,这取决于您的数据和执行计划。理想情况下,上述两个索引之一将帮助数据库执行 ROW_NUMBER
,以及 WHERE
子句。
如果您的数据库版本不支持ROW_NUMBER
,那么您可以继续目前的做法:
SELECT d1.user_id, d1.data, d1.date
FROM datapoint_table d1
INNER JOIN
(
SELECT user_id, MIN(date) AS min_date
FROM datapoint_table
WHERE date > '2019-01-14'
GROUP BY user_id
) d2
ON d1.user_id = d2.user AND d1.date = d2.min_date
WHERE
d1.date > '2019-01-14';
同样,建议的索引至少应该加速 GROUP BY
子查询的执行。
我已经阅读了很多关于 greatest-n-per-group 的帖子,但似乎仍然没有在性能方面找到好的解决方案。我是 运行 10.1.43-MariaDB。
我正在尝试获取给定时间范围内数据值的变化,因此我需要从该时间段获取最早和最新的行。现在需要计算的时间范围内的最大行数约为 700k,并且只会增加。现在我刚刚做了两个查询,一个是最近的,一个是最早的,但即使这样,目前的性能也很慢。 table 看起来像这样:
user_id data date
4567 109 28/06/2019 11:04:45
4252 309 18/06/2019 11:04:45
4567 77 18/02/2019 11:04:45
7893 1123 22/06/2019 11:04:45
4252 303 11/06/2019 11:04:45
4252 317 19/06/2019 11:04:45
日期和 user_id 列已编入索引。没有排序,如果有区别的话,行在数据库中没有任何特定的顺序。
我在这个问题上得到的最深入的查询是针对当前年份的查询(700k 数据点):
SELECT user_id,
MIN(date) as date, data
FROM datapoint_table
WHERE date >= '2019-01-14'
GROUP BY user_id
这给了我正确的日期和 user_id 在大约 ~0.05 秒内非常快。但是就像每组最大 n 的常见问题一样,该行的其余部分(在本例中为数据)与日期不在同一行。我已经阅读了其他类似的问题并尝试使用这样的子查询:
SELECT a.user_id, a.date, a.data
FROM datapoint_table a
INNER JOIN (
SELECT datapoint_table.user_id,
MIN(date) as date, data
FROM datapoint_table
WHERE date >= '2019-01-01'
GROUP BY user_id
) b ON a.user_id = b.user_id AND a.date = b.date
此查询大约需要 15 秒才能完成并获取正确的数据值。 15 秒太长了,当第一个查询如此之快时,我一定是做错了什么。我还尝试对 user_id 分组的数据进行 (MAX)-(MIN),但它的性能也很慢。
要获得与日期相同的数据值,甚至每个用户的最新和最早数据的差异,什么是更有效的方法?
假设您使用的是 MariaDB 或 MySQL 的最新版本,那么 ROW_NUMBER
可能是为每个用户查找最早记录的最有效方法:
WITH cte AS (
SELECT *, ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY date) rn
FROM datapoint_table
WHERE date > '2019-01-14'
)
SELECT user_id, data, date
FROM cte
WHERE rn = 1;
在上面还可以考虑增加如下索引:
CREATE INDEX ON datapoint_table (user_id, date);
您还可以尝试使用以下列颠倒的变体索引:
CREATE INDEX ON datapoint_table (date, user_id);
尚不清楚哪个版本的索引性能最好,这取决于您的数据和执行计划。理想情况下,上述两个索引之一将帮助数据库执行 ROW_NUMBER
,以及 WHERE
子句。
如果您的数据库版本不支持ROW_NUMBER
,那么您可以继续目前的做法:
SELECT d1.user_id, d1.data, d1.date
FROM datapoint_table d1
INNER JOIN
(
SELECT user_id, MIN(date) AS min_date
FROM datapoint_table
WHERE date > '2019-01-14'
GROUP BY user_id
) d2
ON d1.user_id = d2.user AND d1.date = d2.min_date
WHERE
d1.date > '2019-01-14';
同样,建议的索引至少应该加速 GROUP BY
子查询的执行。