Baz*_*nga 9 c# linq entity-framework entity-framework-4
我希望通过表的ID列检索表的最后一行.我目前使用的是什么:
var x = db.MyTable.OrderByDescending(d => d.ID).FirstOrDefault();
Run Code Online (Sandbox Code Playgroud)
有没有办法以更有效的速度获得相同的结果?
我无法通过整个表格查看此查询.
您是否在ID列上没有索引?
您可以将分析查询的结果添加到您的问题中,因为这不是它应该如何.
以及分析结果,SQL产生了.除了select top 1 * from MyTable order by id desc显式列名和一些别名之外,我看不出它会是什么.除非对该指数进行id扫描,否则它是否有任何指数.
编辑:那承诺的解释.
Linq为我们提供了一组通用接口,在C#和VB.NET的情况下提供了一些关键字支持,用于对返回0或更多项的源进行各种操作(例如内存中集合,数据库调用,XML文档解析)等).
这允许我们表达类似的任务,无论底层来源如何.例如,您的查询包含源代码,但我们可以采用更一般的形式:
public static YourType FinalItem(IQueryable<YourType> source)
{
return source.OrderByDesending(d => d.ID).FirstOrDefault();
}
Run Code Online (Sandbox Code Playgroud)
现在,我们可以做到:
IEnumerable<YourType> l = SomeCallThatGivesUsAList();
var x = FinalItem(db.MyTable);//same as your code.
var y = FinalItem(l);//item in list with highest id.
var z = FinalItem(db.MyTable.Where(d => d.ID % 10 == 0);//item with highest id that ends in zero.
Run Code Online (Sandbox Code Playgroud)
但真正重要的部分是,虽然我们有一种定义我们想要完成的操作的方法,但我们可以隐藏我们的实际实现.
调用OrderByDescending生成一个对象,该对象具有关于其源的信息,以及它将在排序中使用的lambda函数.
该调用FirstOrDefault依次具有该信息,并使用它来获得结果.
在列表中的情况下,实现生产相当于Enumerable基于代码(Queryable和Enumerable互为镜像的公共成员,因为这样做的界面,他们使用如IOrderedQueryable和IOrderedEnumerable等).
这是因为,我们不知道的列表已按照我们关心的顺序(或以相反的顺序)排序,没有比检查每个元素更快的方法.我们可以期望的最好的是O(n)操作,我们可能会得到一个O(n log n)操作 - 取决于是否优化了排序的实现,只能从中获取一个项目*.
或者换句话说,我们可以手动编写只能在枚举上工作的代码的效果比以下方法更有效:
public static YourType FinalItem(IEnumerable<YourType> source)
{
YourType highest = default(YourType);
int highestID = int.MinValue;
foreach(YourType item in source)
{
curID = item.ID;
if(highest == null || curID > highestID)
{
highest = item;
highestID = curID;
}
}
return highest;
}
Run Code Online (Sandbox Code Playgroud)
我们可以通过一些微操作直接处理枚举器来做得稍微好一点,但只是略微增加并且额外的复杂性只会使不太好的示例代码.
因为我们不能手工做得更好,并且因为linq代码对源的了解比我们所知的更多,所以我们可能希望它匹配得最好.它可能做得不太好(再次,取决于我们唯一想要的一个项目的特殊情况是否被认为),但它不会击败它.
然而,这不是 linq将采取的唯一方法.它将采用与内存中可枚举源类似的方法,但您的来源不是这样的.
db.MyTable代表一张桌子.通过它进行枚举,我们得到的SQL查询的结果或多或少等同于:
SELECT * FROM MyTable
Run Code Online (Sandbox Code Playgroud)
然而,db.MyTable.OrderByDescending(d => d.ID)是不是叫了出来,然后排序在内存中的结果等同.因为查询在执行时会作为一个整体进行处理,所以我们实际上得到的SQL查询的结果或多或少如下:
SELECT * FROM MyTable ORDER BY id DESC
Run Code Online (Sandbox Code Playgroud)
最后,整个查询db.MyTable.OrderByDescending(d => d.ID).FirstOrDefault()会产生如下查询:
SELECT TOP 1 * FROM MyTable ORDER BY id DESC
Run Code Online (Sandbox Code Playgroud)
要么
SELECT * FROM MyTable ORDER BY id DESC LIMIT 1
Run Code Online (Sandbox Code Playgroud)
取决于您使用的数据库服务器类型.然后结果传递给相当于以下基于ADO.NET的代码的代码:
return dataReader.Read() ?
new MyType{ID = dataReader.GetInt32(0), dataReader.GetInt32(1), dataReader.GetString(2)}//or similar
: null;
Run Code Online (Sandbox Code Playgroud)
你不可能变得更好.
至于那个SQL查询.如果id列上有索引(并且因为它看起来像主键,当然应该有),那么该索引将用于非常快速地找到有问题的行,而不是检查每一行.
总之,因为不同的linq提供者使用不同的方法来完成查询,所以他们都可以尽可能以最好的方式尽力而为.当然,在一个不完美的世界里,我们无疑会发现有些人比其他人更好.更重要的是,他们甚至可以为不同条件选择最佳方法.其中一个例子是,与数据库相关的提供程序可以选择不同的SQL来利用不同版本数据库的功能.另一个是Count()在内存枚举中使用的版本的实现有点像这样;
public static int Count<T>(this IEnumerable<T> source)
{
var asCollT = source as ICollection<T>;
if(asCollT != null)
return asCollT.Count;
var asColl = source as ICollection;
if(asColl != null)
return asColl.Count;
int tally = 0;
foreach(T item in source)
++tally;
return tally;
}
Run Code Online (Sandbox Code Playgroud)
这是一个更简单的情况(在我的例子中再次进行了一点简化,我显示的是这个想法而不是实际的代码),但它显示了代码的基本原理,当它们可用时利用更有效的方法 - 数组的O(1)长度和Count集合上的属性有时为O(1),并且它不像我们在O(n)的情况下使事情变得更糟 - 然后当它们不可用时倒退一种效率较低但功能较强的方法.
所有这一切的结果是,Linq在性能方面往往会给予很好的帮助.
现在,一个体面的编码器应该能够在大多数情况下匹配或击败任何给定情况的方法†,即使Linq提出了完美的方法,它本身也有一些开销.
但是,在整个项目的范围内,使用Linq意味着我们可以简明地创建与相对受限数量的明确定义的实体相关的合理有效的代码(就数据库而言,通常每个表一个).特别是,使用匿名函数和连接意味着我们获得非常好的查询.考虑:
var result = from a in db.Table1
join b in db.Table2
on a.relatedBs = b.id
select new {a.id, b.name};
Run Code Online (Sandbox Code Playgroud)
在这里,我们忽略了我们不关心的列,并且生成的SQL将执行相同的操作.考虑一下,如果我们创建一个对象,我们会做a,并b涉及与手工编码的DAO类:
aid和b名称的组合,以及运行我们生成实例所需查询的相关代码.a和相关的所有信息b,并与废物一起生活.a和b我们关心的信息,并为其他字段设置默认值.其中,备选方案2将是浪费,可能非常浪费.选项3将有点浪费并且非常容易出错(如果我们不小心尝试在代码中的其他地方使用未正确设置的字段会怎么样?).只有选项1比linq方法产生的效率更高,但这仅仅是一种情况.在一个大项目中,这可能意味着产生几十个甚至几百个或几千个略有不同的类(与编译器不同,我们不一定会发现它们实际上相同的情况).因此,在实践中,linq在效率方面可以为我们带来一些好处.
高效linq的良好政策是:
ToList()或ToArray等把物品拿到内存中时,请考虑你是否真的需要.除非你需要,否则你可以清楚地说明这样做的优势给你,不要.AsEnumerable()over ToList()和其他方法,所以你一次只能抓一个.AsEnumerable()实际上更好(大多数涉及使用GroupBy不在非分组字段上使用聚合,因此实际上没有单个SQL查询他们对应).CompiledQuery可以提供帮助(因为它有自动优化功能可以覆盖它帮助的一些情况),因此可能会有所帮助,但通常最好将其从第一种方法中删除,然后再使用它在热点,这是效率问题.*AFAIK,这个特殊的优化没有应用,但是我们现在讨论的是最好的实现,所以无论是,是不是,或者仅仅是某些版本.
†我承认,虽然Linq2SQL经常会产生我不会想到的使用APPLY的查询,因为我习惯于在2005年之前考虑如何在SQLServer版本中编写查询,而代码没有那些排序人类倾向于遵循旧习惯.它几乎教我如何使用APPLY.
| 归档时间: |
|
| 查看次数: |
507 次 |
| 最近记录: |