在Linq2SQL中,如何在单个查询中获取记录加上序列中的上一个和下一个?

时间:2011-01-24 17:37:14

标签: linq performance linq-to-sql

给定一个日期,在该日期之前查询最后一条记录的最有效方法是什么,任何等于该日期的记录,以及该日期之后的下一条记录。

它应该在功能上等同于这样的查询:

from asset in Assets
where asset.Id == assetId
select new {
    Previous = (from a in a.Orders where a.Date < myDate orderby a.Date descending select a).FirstOrDefault(),
    Current = (from a in a.Orders where a.Date == myDate select a).SingleOrDefault(),
    Next = (from a in a.Orders where a.Date > myDate orderby a.Date select a).FirstOrDefault()
}

按原样,此查询会运行三个查询,并且可能需要通过myDate对数据集进行三次排序才能执行此操作。

一些类似的问题:

4 个答案:

答案 0 :(得分:5)

提供“最有效”的查询取决于您的效率是什么意思。

如果您想要对数据库进行单一查询,按日期进行单一排序,最后按日期进行快速查询,那么我建议以下可能是最有效的。 : - )

var orders =
    (from a in Assets
     where a.Id == assetId
     from o in a.Orders
     orderby o.Date
     select o).ToArray();

var previous = orders.LastOrDefault(o => o.Date < myDate);
var current = orders.SingleOrDefault(o => o.Date == myDate);
var next = orders.FirstOrDefault(o => o.Date > myDate);

这应该查询数据库一次以查找与所需资产Id关联的订单,按日期对它们进行排序,并将它们作为数组返回到内存中。由于这是在内存中,现在看起来非常快速地寻找当前的,先前的&amp;指定日期的下一个记录。

答案 1 :(得分:2)

您的订单表是否有序列ID字段?如果是这样,您可以使用:

from asset in Assets
where asset.Id == assetID
let current = asset.Orders.Where(x => x.Date == myDate).FirstOrDefault()
where current != null
let previous = asset.Orders.Where(x => x.id == current.id - 1).FirstOrDefault()
let next = asset.Orders.Where(x => x.id == current.id + 1).FirstOrDefault()
select new {
    Previous = previous,
    Current = current,
    Next = next
};

如果没有,那么代码就会多一些:

from asset in Assets
where asset.Id == assetID
let current = asset.Orders.Where(x => x.Date == myDate).FirstOrDefault()
where current != null
let previous = asset.Orders.Where(x => x.Date < current.Date).OrderByDescending(x => x.Date).FirstOrDefault()
let next = asset.Orders.Where(x => x.Date > current.Date).OrderBy(x => x.Date).FirstOrDefault()
select new {
    Previous = previous,
    Current = current,
    Next = next
};

应该编译成一个利用子查询的SQL查询。 IE:数据库服务器将执行多个查询,但您的客户端程序只提交一个。

编辑如果您的订单表有连续ID,则另一个想法是:

var sample = (from asset in Assets
              where asset.Id == assetID
              let current = asset.Orders.Where(x => x.Date == myDate).FirstOrDefault()
              where current != null
              from order in asset.Orders
              where order.Id == current.id - 1
              select order)
             .Take(3)
             .ToArray();

var Previous = sample[0];
var Current = sample[1];
var Next = sample[2];

答案 2 :(得分:1)

其他答案,例如,SkipWhile等非常慢。祝你好运^^

//Current Record
var query
= (from item in db.Employee
   where item.UserName.Equals(_username)
   select item).SingleOrDefault();

//Next Record
var query
= (from item in db.Employee
   where item.UserName.CompareTo(_username) > 0
   select item).FirstOrDefault();

//Previous Record
var query
= (from item in db.Employee
   where item.UserName.CompareTo(_username) < 0
   orderby item.UserName Descending
   select item).FirstOrDefault();

答案 3 :(得分:0)

几乎相同,但SQL查询计划可能不同。

var q = 
 from asset in Assets
 where asset.Id == assetID
 select new 
 {
     Previous = asset.Orders.where(a => a.Date == asset.Orders.Where(x => x.Date < myDate).Max(x => x.Date)).FirstOrDefault(),
     Current = asset.Orders.Where(x => x.Date == myDate).FirstOrDefault(),
     Next = asset.Orders.where(a => a.Date == asset.Orders.Where(x => x.Date > myDate).Min(x => x.Date)).FirstOrDefault()
 };