使用EF4仅获得SQL表的最后一行的最有效方法是什么? - c#

我正在通过表的ID列检索表的最后一行。我目前正在使用的作品:

var x = db.MyTable.OrderByDescending(d => d.ID).FirstOrDefault();

有什么方法可以更快地获得相同的结果吗?

参考方案

我看不到整个表都在查询。

您在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();
}

现在,我们可以做:

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.

但是真正重要的部分是,尽管我们有一种方法可以定义我们想要完成的操作,但是我们可以将实际的实现隐藏起来。

调用OrderByDescending会生成一个对象,该对象的源信息以及将在排序中使用的lambda函数。

FirstOrDefault的调用又具有有关此信息,并使用它来获取结果。

对于列表,实现是生成等效的基于Enumerable的代码(QueryableEnumerable彼此镜像公共成员,它们使用的接口(例如IOrderedQueryableIOrderedEnumerable和依此类推)。

这是因为,对于我们不知道的列表,已经按照我们关心的顺序(或相反的顺序)进行了排序,因此没有比检查每个元素更快的方法了。我们可以期望的最好的结果是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;
}

我们可以通过一些微选项直接处理枚举数来做得更好,但是只有一点点,并且额外的复杂性只会使效果不佳的示例代码成为可能。

由于我们不能做任何比这更好的事情,并且由于linq代码对源代码的了解比我们更多,所以这是我们可能希望匹配它的最好方法。它可能做得不太好(再次,取决于是否只考虑了我们只想要一项的特殊情况),但它不会胜过它。

但是,这不是linq会采用的唯一方法。对于内存中可枚举的源,它将采用可比的方法,但是您的源并非如此。

db.MyTable代表一个表。枚举它可以使我们获得的SQL查询结果大致等于:

SELECT * FROM MyTable

但是,db.MyTable.OrderByDescending(d => d.ID)不等同于调用它,然后在内存中对结果进行排序。因为查询在执行时会被整体处理,所以实际上我们获得的SQL查询结果大致如下:

SELECT * FROM MyTable ORDER BY id DESC

最后,整个查询db.MyTable.OrderByDescending(d => d.ID).FirstOrDefault()产生的查询如下:

SELECT TOP 1 * FROM MyTable ORDER BY id DESC

要么

SELECT * FROM MyTable ORDER BY id DESC LIMIT 1

取决于您使用的是哪种数据库服务器。然后将结果传递给等效于以下基于ADO.NET的代码的代码:

return dataReader.Read() ?
  new MyType{ID = dataReader.GetInt32(0), dataReader.GetInt32(1), dataReader.GetString(2)}//or similar
  : null;

你不会变得更好。

至于那个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;
}

这是较简单的情况之一(在这里的示例中再次进行了简化,我展示的不是实际的代码),但是它显示了代码的基本原理,并在可行时利用了更有效的方法-数组的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};

在这里,我们忽略了我们不在乎的列,并且生成的SQL将执行相同的操作。考虑一下如果创建与手工编码的DAO类相关的ab对象的方法:

创建一个新类来表示a的ID和b的名称的组合,以及用于运行我们需要产生实例的查询的相关代码。
运行查询以获取有关每个a和相关的b的所有信息,并避免浪费。
运行查询以获取有关我们关心的每个ab的信息,并为其他字段设置默认值。

其中,选择2将是浪费的,也许是非常浪费的。选项3有点浪费,而且很容易出错(如果我们不小心尝试使用代码中未正确设置的其他字段,该怎么办?)。只有选项1比linq方法产生的效率更高,但这只是一种情况。在一个大型项目中,这可能意味着产生数十个甚至数百个或数千个稍有不同的类(并且与编译器不同,我们不一定会发现它们实际上是相同的情况)。因此,在实践中,linq可以在效率方面为我们提供一些帮助。

有效的linq的好的策略是:

尽可能保持与您开始的查询类型相同。每当您使用ToList()ToArray等将项目捕获到内存中时,请考虑是否确实需要这样做。除非您需要或可以明确说明这样做所带来的好处,否则就不要这样做。
如果确实需要转移到内存中进行处理,则优先使用AsEnumerable()而不是ToList()和其他方法,因此一次只能抓取一个。
使用SQLProfiler或类似方法检查长时间运行的查询。在少数情况下,此处的策略1是错误的,并且使用AsEnumerable()转移到内存实际上更好(大多数与GroupBy的使用有关,这些用法不对非分组字段使用聚合,因此不实际上只有一个与之对应的SQL查询)。
如果多次命中一个复杂的查询,则CompiledQuery可以提供帮助(对于4.5来说则更少,因为它具有自动优化功能,可以解决某些情况),但是通常最好将其排除在第一种方法之外,然后仅在存在效率问题的热点中使用它。
您可以使EF运行任意SQL,但除非有很大收获,否则应避免使用它,因为过多的此类代码会在整个Gives中使用linq方法降低一致的可读性(我不得不说,我认为Linq2SQL在调用存储过程甚至击败EF时关于调用UDF的更多信息,但即使在这里仍然适用-从仅查看代码之间的相互关系还不清楚。

* AFAIK,没有应用此特定的优化,但目前我们正在讨论最佳的实现方式,因此无论是,不是或仅在某些版本中,都没有关系。

†尽管,我会承认Linq2SQL经常会生成使用我不会想到的APPLY的查询,因为我曾经想过如何在2005年引入SQL Server版本之前编写查询,而代码却没有这种排序人类倾向与旧习惯。它几乎教会了我如何使用APPLY。

Entity Framework DbEntityEntry>'不包含Where的定义 - c#

此代码狙击手来自Adding CreatedDate to an entity using Entity Framework 5 Code First public override int SaveChanges() { DateTime saveTime = DateTime.Now; foreach (var entry in this.ChangeT…

使用Entity Framework Core(2.1)调用标量函数的最佳实践 - c#

我经常需要从Web应用程序(ASP.NET Core / EF Core)中调用在SQL Server上定义的标量函数。由于这些函数只是简单的辅助函数,因此我也使用了许多辅助函数,因此我使用了通用模式来调用这些标量函数-借助EF Core 2.1可用的新查询类型。由于我是EF Core的新手,所以我的问题是这种模式是否会引起问题,并且/或者是否存在调用标量函…

Play Framework 2中的系统类加载器 - java

我使用的是Play 2.2.2,我有一个外部jar,它试图从同一jar的根目录加载XML资源。它使用System.class.getClassLoader().getResource("/Blabla.xml")这样做。这失败了,因为显然Play拥有一个奇怪的类加载器层次结构:ReloadableClassLoader和几个父级。该层次结…

如何以编程方式将ListView滚动到最后一个元素-Compact Framework - c#

我正在使用Windows Mobile 6.1上的3.5 Compact Framework开发应用程序。我有一个ListView,添加项目时想自动滚动此列表。我能怎么做? 参考方案 listView.EnsureVisible(listView.Items.Count - 1);

如何在实体框架代码优先迁移中设置小数精度和小数位数 - c#

我正在使用实体框架迁移,并且需要设置实体中十进制属性的精度和小数位数。我只想对此单个十进制属性(而不是所有十进制属性)执行此操作。我已经重写了OnModelCreating方法,以默认将小数设置为(18,2)。我需要此属性为(22,5)。例如public class AdditionalCharge { public decimal? Rate { get;…