我有大量数据,4000 行以上。每行都与每天收集的指标数据相关联。指标表超过 110 万行。
例如:
物品表:
身份证 | 项目 |
---|---|
1 | 你好 |
2 | 世界 |
... | ... |
公制表:
身份证 | 商品编号 | 创建日期 | 公制1 | 公制2 |
---|---|---|---|---|
1 | 1 | 2024-04-20 | 34 | 21 |
2 | 1 | 2024-04-21 | 54 | 12 |
3 | 1 | 2024-04-22 | 32 | 23 |
4 | 2 | 2024-04-20 | 53 | 43 |
5 | 2 | 2024-04-21 | 54 | 23 |
6 | 2 | 2024-04-22 | 12 | 45 |
... | ... | ... | ... | ... |
我需要向用户显示项目表,并附加一个“排名”列,排名将基于指标 1 数据的平均值,使问题更加复杂的是用户可以按列排序并设置日期范围。
为了计算排名,我做了以下操作:
虽然这按预期工作,但它的计算成本非常高,并且需要很长时间,因为它必须迭代 4000 多行以及每行的一组指标,为了进一步添加这一点,有一个指示器可以指示它是上升还是下降通过将当前日期范围(例如 7 天范围)与其之前的日期范围(例如前 7 天)进行比较来排名,这实际上使负载加倍。
数据位于 SQL 服务器上。
有没有办法让用户的性能更高/更快? 对我来说,主要问题是有太多可变变量无法缓存/存储值。
能够按日期范围进行过滤,意味着我无法存储数据并且计算必须内联完成。
我所拥有的可以工作并且满足要求,但是它非常慢,40秒左右。 对于页面加载等更常见的请求,有预先构建的缓存。
这有效,但同样,如果用户更改 orderby 值或日期范围,则必须计算所有内容。
有没有可用的技术可以加速这种过程? 是否有任何方法可以以不同的方式存储数据以加快计算过程?
编辑
用.Net core编写的项目,使用Entity Framework
EF 查询的粗略 sudo 代码:
var dateRangeQuery = FROM Metrics in DB.Metrics
WHERE CreatedDate > FromDate AND CreatedDate < ToDate
GROUP Metrics BY Metrics.ItemId
INTO Result select new
{
ItemId = Result.Key,
Metric1 = Result.Average(x => x.Metric1),
Metric2 = Result.Average(x => x.Metric2)
}
var joinToItemsQuery = DB.Items
Include Metrics
JOIN dateRangeQuery
WHERE Items.Id == dateRangeQuery.ItemID
var orderQuery = joinToItemsQuery ORDERBY dateRangeQuery.Metric1
SELECT Items
然后可以通过在 orderQuery 结果中查找项目的索引来获得排名。
写上面的内容帮助我发现了一些不必要的代码,好老橡皮鸭。
据我所知,不需要使用 dateRangeQuery 连接 Items,所以我将其删除,应该可以节省一些时间。
上面有 2 次运行两次,一次针对当前日期范围,一次针对上一个日期范围进行比较
如果您只是创建正确的索引,您的问题就会消失。
create unique index IdItemIdIdx on Metrics (Id, ItemId);
现在一周的数据需要 28,000 多次索引查找,而不是一遍又一遍地扫描数百万行。
如果您想在大日期范围内提高效率,请将
Metrics
表更改为 CumulativeMetrics
,其中包含以下列:
Id ItemId CreatedDate CountOfDates SumOfMetric1 SumOfMetric2
所有这些总和都是从开始到创建日期的所有时间。
现在您只需查看开始日期和结束日期的行即可计算日期范围。因此,现在我们可以为每个
Id
获取 3 个日期,以获取当前日期范围和上一个日期范围。无论您的日期范围有多大,这都有效。
这使得大日期范围变得高效。但请注意注意事项。您现在依赖于已正确预先计算的此表。处理超出表格日期范围的日期范围需要一定的复杂性。但如果有人查询一个月或一年的时间段,你会得到很好的加速。