这个Any是好还是不好?

时间:2015-02-08 19:40:51

标签: c# entity-framework

我正在使用EF6,我希望将表中的记录放在一组ID中。

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

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

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

这个linq exrepsion生成的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是一个使用很多子查询和联合的“where exists”。

第二个选项是包含。

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))

包含被翻译成“where in”,但查询要复杂得多。

我已经读过任何它用得更快,所以我怀疑是否有,虽然乍一看更复杂,但是更快或不是。

非常感谢。

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

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。

所以第二种选择,包含污染,速度较慢。

3 个答案:

答案 0 :(得分:2)

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

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

答案 1 :(得分:1)

如果您知道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

答案 2 :(得分:1)

您可以根据查询的形状判断Any根本不可扩展。它不会占用myIDS中的许多元素(可能大约50个)来获取最大嵌套级别已超过的SQL异常。

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

所以我会选择可扩展的解决方案,即使Any的数字较小可能更快。可以Contains甚至better scalable

  

我已经读过任何它用来加快的速度,

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