2011-01-24 1 views
3

С учетом даты, что является наиболее эффективным способом запроса последней записи до этой даты, любой записи, которая равна этой дате, и следующей после этой даты.В Linq2SQL, как мне получить запись плюс предыдущую и следующую последовательность в одном запросе?

Он должен быть функционально эквивалентен запросу, как это:

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 три раза, чтобы сделать это.

Некоторые подобные вопросы:

ответ

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 активов, сортировать их по дате, и возвращать их как массив в памяти. Так как это в памяти, теперь ослепительно быстро искать текущий, предыдущий & следующие записи за указанную дату.

+0

Полезный ответ, в зависимости от размера возврата. Тем не менее, я думаю, что это может быть улучшено с помощью контейнера, который знает, что он отсортирован, поэтому поисковые запросы O (log n) вместо O (n), как они есть в вашем примере. –

+1

@Scott - Да, его можно улучшить. Я сделал тест с 100 000 записей заказов - по одной записи за каждый день с сегодняшнего дня, вернувшейся в апреле 1737 года, - это не вполне реальный пример. Прошло около 7,2 миллисекунды, чтобы запустить, используя метод, указанный в моем ответе. Я также создал метод индекса бисекции, и это заняло около 0,7 миллисекунды. Используя 'Array.FindIndex', оно заняло 0.83 миллисекунды. Оба этих последних двух подхода к коду были менее читабельными. С практической точки зрения, будет ли эта разница в производительности важна для вас? – Enigmativity

+0

Наверное, нет. ;) Спасибо за бенчмаркинг! –

2

Имеет ли таблица ваших заказов порядковое поле идентификатора? Если да, то вы могли бы сделать это с:

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: сервер базы данных выполнит несколько запросов, но ваша клиентская программа только отправляет их.

Редактировать Еще одна идея, которая будет работать, если ваша таблица заказа были последовательные идентификаторы:

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]; 
+0

У меня нет последовательного идентификатора, к сожалению. Разве ваш пример (№ 2) не генерирует то же, что я предложил? –

+0

Я не знаю, не настроив макет среды, но я считаю, что он должен быть скомпилирован в один SQL-запрос от вашего клиента, который просто использует подзапросы. К сожалению, если у вас нет последовательного идентификатора, я думаю, что это лучшее, что вы можете сделать - вы лишили себя средств для достижения цели более эффективно. –

+0

Возможно, что так - последовательный идентификатор невозможен (или не стоит того, в любом случае) в ситуации, хотя данные не добавляются в каком-либо гарантированном порядке. –

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