Linq:高性能数据库查询仅查询每个第 n 个元素
Posted
技术标签:
【中文标题】Linq:高性能数据库查询仅查询每个第 n 个元素【英文标题】:Linq: Performant database query only querying every nth element 【发布时间】:2019-12-08 17:07:09 【问题描述】:我正在从事一个个人项目,在该项目中我需要一些关于对数据库进行高性能 Linq 查询的帮助。有问题的数据库可能有数百万个日志条目,并且通过 API (Asp),我希望可以选择仅将这些日志的代表性子集返回到图形界面。
这是有问题的方法:
public IEnumerable<Log> GetByParameter(int ParameterID,DateTime timeStart, DateTime timeEnd)
return _context.Logs.Where
(a => a.ParameterID == ParameterID &&
(DateTime.Compare(a.LogDate,timeStart) > 0 && DateTime.Compare(a.LogDate,timeEnd) < 0)).ToList();
请注意,该方法接受两个 DateTimes 作为参数,这会产生应查询日志的时间范围。
我想像这样扩充这个方法:
public IEnumerable<Log> GetByParameter(int ParameterID,DateTime timeStart, DateTime timeEnd, int limit)
例如,给定传递的参数,数据库可能包含 200 万个条目,而 API 使用者的“限制”可能是 40000 个条目。因此:
numberOfEntries/limit = n
2*106 / 4*104 = 50
在此示例中,我希望将每 50 个元素返回给 API 的使用者,元素之间的时间间隔均匀。
一种简单的方法是在给定参数的情况下查询整个表,然后过滤掉,但这似乎很混乱,与这种方法有点对立,也可能非常无效。
所以这是我的问题:有没有办法编写一个查询,使它只查询每第 N 行的数据库?
提前致谢!
【问题讨论】:
你用的是什么数据库? SQL 服务器? 是否有理由限制您使用 LINQ (EF?)? @Oliver 并不特别,除了我编写的其他代码是使用 Linq 之外,我想学习语法。我可以访问数据库,也可以编写 SP Linq 有Skip(i)
和 Take(i)
。有了它,你就可以分页了。
Here 对此有一个答案,您也可以在Queryable
上使用它。但是,您必须对其进行测试,因为它可能会在客户端执行。至于你的实际要求,我认为你不应该做你想做的事。您正在尝试任意下采样,这会导致许多其他问题。正确的做法是对结果进行分页。就性能而言,当您处理这么多行时,这主要取决于数据和索引等的性质,而不是 LINQ。
【参考方案1】:
您可以使用 SQL Server 窗口函数(如 row_number)来实现它:
WITH x AS
(
SELECT ROW_NUMBER() over (order by LogDate) as rn, *
FROM MyTable
WHERE
ParameterID = @ParameterID AND
LogDate > @StartDate AND
LogDate < @EndDate
)
SELECT * from X WHERE rn % 50 = 0
在 LINQ 中,您可以尝试使用以下子句:
var data = _context.Logs
.Select((x, i) => new Data = x, Number = i )
.Where(x => x.Number % 50 == 0)
.Select(x => x.Data);
但是要检查实际的执行计划,我估计不会是最优的。
不要忘记在 LogDate 上创建索引。
老实说,我不确定 SQL Server 是否是存储日志的好选择,我想使用 Elastic 之类的东西。
【讨论】:
【参考方案2】:您可以采取的一种方法是在某种索引上使用模数。如果您已经有一个自动生成的Id
,则可以使用它 - 但它并不理想,因为您不能依赖它是连续的。
您可以使用RANK()
在视图中创建索引列,但遗憾的是您不能直接在 EF 代码中使用 RANK()
。
类似于以下内容:
var interval = 5;
return _context.Logs
.Where(a =>
a.ParameterID == ParameterID &&
(
DateTime.Compare(a.LogDate,timeStart) > 0 &&
DateTime.Compare(a.LogDate,timeEnd) < 0) &&
a.Id % interval == 0).ToList(); //Filter on modulus of an index
在这种情况下,我个人会用 SQL 编写查询。
【讨论】:
以上是关于Linq:高性能数据库查询仅查询每个第 n 个元素的主要内容,如果未能解决你的问题,请参考以下文章
(转)LINQ查询操作符之DistinctUnionConcatIntersectExcept