我想知道下一件事:
我可以使用LINQ To Entities查询我的数据库,如下所示:
GetAll().Where(
x => x.SomeProperty == 'Yes'
&& x.SomeOtherProperty == 'No')
.ToList();
虽然我看到我的一些同事更改了这两个WHERE子句,如下所示:
GetAll().Where(x => x.SomeProperty == 'Yes')
.Where(x.SomeOtherProperty == 'No')
.ToList();
两个查询都应该产生相同的输出,但我想知道这两个中的一个是否有任何优点/缺点。例如,一种方法是否产生较慢的数据库查询,或者它们是否会生成相同的sql-query?
答案 0 :(得分:3)
我使用this article中描述的模型和上下文设置了一个测试项目,并根据您问题中的模式记录了两个不同查询的SQL。我提出的问题是:
db.Blogs
.Where(b => b.BlogId == 0)
.Where(b => b.Name == "Foo");
和
db.Blogs
.Where(b => b.BlogId == 0 && b.Name == "Foo");
两个查询生成的SQL都是相同的:
SELECT
[Extent1].[BlogId] AS [BlogId],
[Extent1].[Name] AS [Name]
FROM [dbo].[Blogs] AS [Extent1]
WHERE (0 = [Extent1].[BlogId]) AND (N'Foo' = [Extent1].[Name])
所以看起来(至少对于像这样的简单情况),没有显着的性能差异。我想你可以说,如果你使用多Where
方法,那么表达访问者需要花费更长的时间来检查你的树,但从长远来看这可以忽略不计。
答案 1 :(得分:0)
我在LINQPad中创建了以下测试用例。
void Main()
{
var listOfObjects = Enumerable.Range(0, 100000).Select(x => new TestClass() { SomeProperty = x.ToString() }).ToArray();
var iterations = DateTime.UtcNow.Day * 50;
Console.WriteLine("Doing {0} iterations", iterations);
var sw = Stopwatch.StartNew();
for(int i = 0; i < iterations; i++)
{
var filteredList = listOfObjects.Where(x => x.SomeProperty.Contains('0') && x.SomeProperty.Contains('1')).ToArray();
}
sw.Stop();
Console.WriteLine(sw.ElapsedMilliseconds);
sw.Reset();
sw.Start();
for(int i = 0; i < iterations; i++)
{
var filteredList = listOfObjects.Where(x => x.SomeProperty.Contains('0')).Where(x => x.SomeProperty.Contains('1')).ToArray();
}
sw.Stop();
Console.WriteLine(sw.ElapsedMilliseconds);
}
public class TestClass
{
public string SomeProperty { get; set; }
public string SomeOtherProperty { get; set; }
}
它为1350次迭代产生以下结果。
&&
子句,19415 ms Where
子句,19596 ms 这似乎表明第一个更快一点。这可能只是编译器在幕后做了一些魔术,但通过计算TestClass
的列表以及运行时的迭代次数,它不应该能够做任何事情。
为了确保使用实际的SQL,你应该看一下IL,但是根据我的看法,看起来有效的代码是相同的。
<Main>b__2:
IL_0000: ldarg.0
IL_0001: callvirt UserQuery+TestClass.get_SomeProperty
IL_0006: ldc.i4.s 30
IL_0008: call System.Linq.Enumerable.Contains
IL_000D: brfalse.s IL_001E
IL_000F: ldarg.0
IL_0010: callvirt UserQuery+TestClass.get_SomeProperty
IL_0015: ldc.i4.s 31
IL_0017: call System.Linq.Enumerable.Contains
IL_001C: br.s IL_001F
IL_001E: ldc.i4.0
IL_001F: nop
IL_0020: stloc.0 // CS$1$0000
IL_0021: br.s IL_0023
IL_0023: ldloc.0 // CS$1$0000
IL_0024: ret
<Main>b__3:
IL_0000: ldarg.0
IL_0001: callvirt UserQuery+TestClass.get_SomeProperty
IL_0006: ldc.i4.s 30
IL_0008: call System.Linq.Enumerable.Contains
IL_000D: stloc.0 // CS$1$0000
IL_000E: br.s IL_0010
IL_0010: ldloc.0 // CS$1$0000
IL_0011: ret
<Main>b__4:
IL_0000: ldarg.0
IL_0001: callvirt UserQuery+TestClass.get_SomeProperty
IL_0006: ldc.i4.s 31
IL_0008: call System.Linq.Enumerable.Contains
IL_000D: stloc.0 // CS$1$0000
IL_000E: br.s IL_0010
IL_0010: ldloc.0 // CS$1$0000
IL_0011: ret
使用Where
的{{1}}子句是IL的第一位,显示匿名方法中对&&
的两次调用。使用double Contains
子句的测试调用两个不同的匿名方法,但它们看起来是相同的,除了传递给Where
的参数。查看main方法,我们可以看到调用,并看到开销很小。当使用两个Contains
子句时,它会导致稍慢的代码。
我的建议是针对实际的SQL数据库运行测试(就像我上面编写的那样),看看性能是什么样的。生成的IL可能几乎相同,但在转换为SQL时可能会有很大的不同。