在没有分组的情况下,您将如何在单个 linq 查询中执行此操作?
如果我必须这样做,那么我会这样做:
var minMax = (from s0 in sections
from s1 in sections
orderby s0.StartDate, s1.EndDate descending
select new {s0.StartDate, s1.EndDate}).FirstOrDefault();
但我也会根据相关提供商考虑性能影响。
在数据库上,我希望这会变成这样:
SELECT s0.StartDate, s1.EndDate
FROM Sections AS s0
CROSS JOIN Sections AS s1
ORDER BY created ASC, EndDate DESC
LIMIT 1
或
SELECT TOP 1 s0.StartDate, s1.EndDate
FROM Sections AS s0, Sections AS s1
ORDER BY created ASC, EndDate DESC
取决于数据库类型。反过来如何执行很可能是两个表扫描,但如果我要关心这些日期,我会在这些列上有索引,所以它应该是每个索引末尾的两个索引查找扫描,所以我预计它会很快。
我有这些对象的列表
如果我很在意性能,我就不会使用 Linq。
但我想使用一个 linq 查询,所以我知道我只迭代列表一次
这就是我不使用 linq 的原因。由于 linq 中没有任何东西旨在处理这种特殊情况,因此它会遇到更糟糕的组合。实际上它会比 2 次迭代更糟糕,它会是 N +1 次迭代,其中 N 是 Sections 中的元素数。 Linq 提供程序很好,但它们并不神奇。
如果我真的希望能够在 Linq 中执行此操作,例如我有时针对内存中的列表执行此操作,有时针对数据库等执行此操作,我会添加我自己的方法来尽可能以最好的方式执行每个操作:
public static Tuple<DateTime, DateTime?> MinStartMaxEnd(this IQueryable<Section> source)
{
if(source == null)
return null;
var minMax = (from s0 in source
from s1 in source
orderby s0.StartDate, s1.EndDate descending
select new {s0.StartDate, s1.EndDate}).FirstOrDefault();
return minMax == null ? null : Tuple.Create(minMax.StartDate, minMax.EndDate);
}
public static Tuple<DateTime, DateTime?> MinStartMaxEnd(this IEnumerable<Section> source)
{
if(source != null)
using(var en = source.GetEnumerator())
if(en.MoveNext())
{
var cur = en.Current;
var start = cur.StartDate;
var end = cur.EndDate;
while(en.MoveNext())
{
cur = en.Current;
if(cur.StartDate < start)
start = cur.StartDate;
if(cur.EndDate.HasValue && (!end.HasValue || cur.EndDate > end))
end = cur.EndDate;
}
return Tuple.Create(start, end);
}
return null;
}
但我想使用一个 linq 查询,所以我知道我只迭代列表一次
回到这个话题。 Linq 不承诺对列表进行一次迭代。它有时可以这样做(或根本不迭代)。它可以调用数据库查询,进而将概念上的几次迭代转换为一两次(在 CTE 中很常见)。它可以生成对各种类似但不完全相同的查询非常有效的代码,而手工编码的替代方法是要么遭受大量浪费,要么编写大量类似但 -不太一样的方法。
但是,如果您假设 Linq 给您一次通过,它也可以隐藏一些 N+1 或 N*N 行为,看起来要少得多。如果您需要特定的单通行为,请添加到 Linq;它是可扩展的。