Сделать Linq для Sql генерировать T-SQL с ISNULL вместо COALESCE
У меня есть запрос linq to sql, который возвращает некоторые заказы с ненулевым балансом (на самом деле запрос немного сложнее, но для простоты я пропустил некоторые подробности). Этот запрос также должен возвращать заказы без CardItems (оба подзапроса возвращают NULL в T-SQL, а сравнение двух NULLS дает FALSE, поэтому я конвертирую значения результатов NULL подзапросов в 0 для сравнения).
var q = (from o in db.Orders
where db.Cards(p =>
p.OrderId == o.Id
&& p.Sum + (db.CardItems.Where(i => i.IncomeId == p.Id)
.Sum(i => (double?)i.Amount) ?? 0)
!= (db.CardItems.Where(i => i.DeductId == p.Id)
.Sum(i => (double?)i.Amount) ?? 0)
).Any()
select o);
Проблема, что преобразование выражения Sum (i = > (double?) i.Amount)?? 0 производят COALESCE, что в десять раз медленнее, чем точно такой же запрос T-SQL с заменой COALESCE на ISNULL из-за подзапроса в нем. Есть ли возможность генерировать ISNULL в этой ситуации?
Ответы
Ответ 1
По моему опыту, уловка linq в генерацию SQL, которую вы хотите, в лучшем случае представляет собой проблему. Если у вас есть реализация запроса, которая лучше, чем linq (работает правильно и работает хорошо), продолжайте использовать ее, даже если только для этого одного запроса.
Самый быстрый способ, вероятно, с DataContext.ExecuteQuery<TResult>
. Он даже увлажнит и обработает возвращенные объекты для вас, точно так же, как если бы linq сгенерировал сам запрос.
http://msdn.microsoft.com/en-us/library/bb361109.aspx
Многие люди предпочли бы разместить этот SQL в хранимой процедуре, особенно в производственном коде. Затем вам просто нужно сопоставить хранимую процедуру с вашими объектами linq, и она работает одинаково. Как это часто бывает, ScottGu имеет довольно подробное сообщение о том, как это сделать:
http://weblogs.asp.net/scottgu/archive/2007/08/16/linq-to-sql-part-6-retrieving-data-using-stored-procedures.aspx
Ответ 2
Поскольку вы принимаете строку без суммы, которая должна быть суммирована как ноль, вы можете просто отфильтровать строки без суммы и не беспокоиться об IsNull или объединиться.
&& p.Sum + (db.CardItems.Where(i => i.IncomeId == p.Id)
.Where(i=> i.Amount > 0)
.Sum(i => (double?)i.Amount) ?? 0)
!= (db.CardItems.Where(i => i.DeductId == p.Id)
.Where(i=> i.Amount > 0)
.Sum(i => (double?)i.Amount) ?? 0)
Так как я не знаю ваших объектов, вы даже можете удалить роли (double?) и операторы по умолчанию (??)