【问题标题】:This Any is better or not than this contains?这 Any 是否比 this 包含的更好?
【发布时间】:2015-04-08 12:38:06
【问题描述】:

我正在使用 EF6,我想获取一组 ID 中的表中的记录。

例如,在我的测试中,我使用了 4 个 ID。

我尝试了两个选项,第一个是任何选项。

dbContext.MyTable
.Where(x => myIDS.Any(y=> y == x.MyID));

而这个 linq 表达式生成的 T-SQL 是:

SELECT 
    *
    FROM [dbo].[MiTabla] AS [Extent1]
    WHERE  EXISTS (SELECT 
        1 AS [C1]
        FROM  (SELECT 
            [UnionAll2].[C1] AS [C1]
            FROM  (SELECT 
                [UnionAll1].[C1] AS [C1]
                FROM  (SELECT 
                    cast(130 as bigint) AS [C1]
                    FROM  ( SELECT 1 AS X ) AS [SingleRowTable1]
                UNION ALL
                    SELECT 
                    cast(139 as bigint) AS [C1]
                    FROM  ( SELECT 1 AS X ) AS [SingleRowTable2]) AS [UnionAll1]
            UNION ALL
                SELECT 
                cast(140 as bigint) AS [C1]
                FROM  ( SELECT 1 AS X ) AS [SingleRowTable3]) AS [UnionAll2]
        UNION ALL
            SELECT 
            cast(141 as bigint) AS [C1]
            FROM  ( SELECT 1 AS X ) AS [SingleRowTable4]) AS [UnionAll3]
        WHERE [UnionAll3].[C1] = [Extent1].[MiID]
    )

可以看出,T-SQL 是一个“存在的地方”,它使用了许多子查询和联合。

第二个选项是包含。

dbContext.MyTable
.Where(x => myIDS.Contains(x.MiID));

还有 T-SQL:

SELECT 
    *
    FROM [dbo].[MiTabla] AS [Extent1]
    WHERE [Extent1].[MiID] IN (cast(130 as bigint), cast(139 as bigint), cast(140 as bigint), cast(141 as bigint))

包含被翻译成“在哪里”,但查询要简单得多。

我读过 any 它曾经更快,所以我怀疑 any 虽然乍一看更复杂,但是否更快。

非常感谢。

编辑:我有一些测试(我不知道这是否是测试的最佳方法)。

System.Diagnostics.Stopwatch miswContains = new System.Diagnostics.Stopwatch();
                        miswContains.Start();
                        for (int i = 0; i < 100; i++)
                        {
                            IQueryable<MyTable> iq = dbContext.MyTable
                            .Where(x => myIDS.Contains(x.MyID));

                            iq.ToArrayAsync();
                        }
                        miswContains.Stop();



                        System.Diagnostics.Stopwatch miswAny = new System.Diagnostics.Stopwatch();
                        miswAny.Start();
                        for (int i = 0; i < 20; i++)
                        {
                            IQueryable<MyTable> iq = dbContext.Mytable
                            .Where(x => myIDS.Any(y => y == x.MyID));

                            iq.ToArrayAsync();
                        }
                        miswAny.Stop();

结果是miswAny大约是850ms,miswContains大约是4251ms。

所以第二个选项,带有 contaions,速度较慢。

【问题讨论】:

  • Entity Framework 生成了带有select * 的代码?类型的 ID 是否长?你怎么知道结果是否没有缓存,你是否尝试交换查询顺序?

标签: c# entity-framework


【解决方案1】:

如果您的MiTabla.MiID索引中,您的第二个选项是我能想到的最快解决方案(至少对于不是非常大的 id 数组)。

如果您想了解更多关于in 子句性能的信息:Is SQL IN bad for performance?

【讨论】:

  • 我已编辑原始帖子以添加测试。但我不知道这是否是测试这种情况的最佳方法。
  • 不,不是,因为您没有考虑一些外部因素,例如 SQL 缓存或网络延迟,尽管它可能会给您一个粗略的估计。无论如何,您要迭代 20 次来进行第二次测试,并迭代 100 次来进行第一次测试(结果表明它快了 5 倍,可能是由于这个原因)。此外,您可能应该使用 await 指令 iq.ToArrayAsync()
【解决方案2】:

如果您知道 ID,那么使用 LINQ2SQL Count() 方法将创建更简洁和更快的 SQL 代码(比 Any 和 Contains):

dbContext.MyTable
.Where(x => myIDS.Count(y=> y == x.MyID) > 0);

为计数生成的 SQL 应如下所示:

DECLARE @p0 Decimal(9,0) = 12345
SELECT COUNT(*) AS [value]
FROM [ids] AS [t0]
WHERE [t0].[id] = @p0

【讨论】:

  • 我知道这种情况下的 ID。但是当我有很多ID时,它会如何查询?
  • 您检查匹配的计数 - 如果计数为正数(如果 ID 不唯一,则至少为 1),则 id 列表包含指定的 id。
【解决方案3】:

您可以通过查询的形状判断 Any 根本不可扩展。不需要myIDS 中的许多元素(可能约为 50 个)即可获得超过最大嵌套级别的 SQL 异常。

Contains 在这方面要好得多。在性能受到严重影响之前,它可以处理数千个元素。

所以我会选择可扩展的解决方案,尽管 Any 可能在数量较少的情况下更快。可以使Contains 甚至better scalable

我已经读过它曾经更快,

在 LINQ-to-objects 中这通常是正确的,因为枚举在第一次命中时停止。但是使用针对 SQL 后端的 LINQ,生成的 SQL 才是最重要的。

【讨论】:

    猜你喜欢
    • 2012-08-19
    • 1970-01-01
    • 2012-02-04
    • 1970-01-01
    • 2011-01-20
    • 2019-04-26
    • 1970-01-01
    • 2020-12-17
    • 2022-08-23
    相关资源
    最近更新 更多