当我在 LINQ 查询中切换 Distinct() 和 OrderBy() 时,性能会发生变化吗?

Does the performance change when I switch Distinct() and OrderBy() in a LINQ Query?

我只是在考虑当我在 LINQ 查询中同时使用 OrderBy()Distinct() 时哪个能为我提供最佳性能。在我看来,它们在速度上是相等的,因为 Distinct() 方法将在内存中使用散列 table,我假设任何 SQL 查询将首先由 .NET 优化在它被执行之前。
我的假设是否正确,或者这两个命令的顺序是否仍然影响 LINQ 的总体性能?
至于它是如何工作的……当您构建 LINQ 查询时,您基本上是在构建表达式树,但尚未执行任何操作。所以调用 MyList.Distinct().OrderBy() 只会生成这棵树,但不会执行它。 (它是延迟的。)只有当你调用另一个函数时 ToList() 才会执行表达式树,并且运行时可以在执行之前优化表达式树。

首先,seq.OrderBy(...).Distinct()seq.Distinct().OrderBy(...)不能保证return相同的结果,因为Distinct()may return an unordered enumeration。 MS 实现可以方便地保留顺序,但如果您将 LINQ 查询传递给数据库,结果可能会以数据库引擎认为合适的任何顺序返回。

其次,在出现大量重复的极端情况下(例如,五个值随机重复 1,000,000 次),您最好在 OrderBy().[=17= 之前执行 Distinct ]

长话短说,如果您希望对结果进行排序,请使用 Distinct().OrderBy(...) 而不管性能如何。

对于 LINQ to 对象,即使我们假设 OrderBy(...).Distinct()Distinct().OrderBy(...) 将 return 相同的结果 (which is not guaranteed),性能也将取决于数据。

如果您有很多重复数据 - 运行 Distinct 首先应该更快。下一个基准测试表明(至少在我的机器上):

public class LinqBench
{
    private static List<int> test = Enumerable.Range(1, 100)
        .SelectMany(i => Enumerable.Repeat(i, 10))
        .Select((i, index) => (i, index))
        .OrderBy(t => t.index % 10)
        .Select(t => t.i)
        .ToList();

    [Benchmark]
    public List<int> OrderByThenDistinct() => test.OrderBy(i => i).Distinct().ToList();

    [Benchmark]
    public List<int>  DistinctThenOrderBy()=> test.Distinct().OrderBy(i => i).ToList();
}

在我的 .Net Core 3.1 机器上它给出:

Method Mean Error StdDev
OrderByThenDistinct 129.74 us 2.120 us 1.879 us
DistinctThenOrderBy 19.58 us 0.384 us 0.794 us

I assume that any SQL query would be optimized first by .NET before it gets > executed.

鉴于以下情况,您认为这将如何运作:

  • 只有 SQL 执行端(服务器)知道这方面的知识(即使用哪些索引)并且有一个查询优化器应该根据 SQL 的统计信息优化执行的查询=50=].
  • 您必须非常确定您不会以任何方式更改结果。

抱歉,这没有意义 - 在没有数据库的所有内部细节的情况下,您几乎无法在 C# 中安全地进行优化,因此将查询发送到数据库进行分析。

因此,OrderBy 或 Distinct(特别是不同的)会影响性能 - 多少取决于 OrderBy 是否可以依赖索引。

or does the order of these two commands still affect the performance of LINQ in general?

这里很有趣(而且你没有举出例子)。

DISTINCT 和 ORDERBY 在 SQL 中以特定顺序排列,无论您在 LINQ 中如何制定它。根据 SQL 定义,只有一种允许的语法。 LINQ 将查询放在一起并对其进行优化。如果您查看语法,DISTINCT(至少是 SQL 服务器的 SQL 术语)和 OrderBy 有一个特定的位置。

另一边...

.Distinct().OrderBy() 和 .OrderBy().Distinct()

有不同的结果。它们可以在 SQL 中完成(您可以将 Distinct 的输出用作虚拟 table 然后您可以订购),但它们具有不同的语义。除非你认为 LINQ 会神奇地读懂你的想法,否则编译器除了假设你有能力编写你所做的事情(只要它是合法的)并按照你给出的顺序执行这些步骤之外没有任何上下文。

例外:可查询中 Distinct 的文档很清楚,这还没有完成:

https://docs.microsoft.com/en-us/dotnet/api/system.linq.queryable.distinct?redirectedfrom=MSDN&view=net-5.0#System_Linq_Queryable_Distinct__1_System_Linq_IQueryable___0__

表示 Distinct returns 是一个无序列表。

因此,它们之间存在根本区别,并不相同。