Почему следующий запрос linq to sql генерирует подзапрос?
Я выполнил следующий запрос:
var list = from book in books
where book.price > 50
select book;
list = list.Take(50);
Я бы ожидал, что приведенное выше сгенерирует что-то вроде:
SELECT top 50 id, title, price, author
FROM Books
WHERE price > 50
но он генерирует:
SELECT
[Limit1].[C1] as [C1]
[Limit1].[id] as [Id],
[Limit1].[title] as [title],
[Limit1].[price] as [price],
[Limit1].[author]
FROM (SELECT TOP (50)
[Extent1].[id] as as [Id],
[Extent1].[title] as [title],
[Extent1].[price] as [price],
[Extent1].[author] as [author]
FROM Books as [Extent1]
WHERE [Extent1].[price] > 50
) AS [Limit1]
Почему вышеупомянутый запрос linq создает подзапрос и откуда приходит C1?
Ответы
Ответ 1
Вы все равно можете сделать его более чистым:
var c = (from co in db.countries
where co.regionID == 5
select co).Take(50);
Это приведет к:
Table(country).Where(co => (co.regionID = Convert(5))).Take(50)
Эквивалент:
SELECT TOP (50) [t0].[countryID], [t0].[regionID], [t0].[countryName], [t0].[code]
FROM [dbo].[countries] AS [t0]
WHERE [t0].[regionID] = 5
EDIT: Комментарии, это не обязательно потому, что с помощью отдельного Take() вы все равно можете использовать его следующим образом:
var c = (from co in db.countries
where co.regionID == 5
select co);
var l = c.Take(50).ToList();
И результат будет таким же, как и раньше.
SELECT TOP (50) [t0].[countryID], [t0].[regionID], [t0].[countryName], [t0].[code]
FROM [dbo].[countries] AS [t0]
WHERE [t0].[regionID] = @p0
Тот факт, что вы написали IQueryable = IQueryable.Take(50)
, является сложной частью здесь.
Ответ 2
Отказ от ответственности: Я никогда не использовал LINQ раньше...
Мое предположение - поддержка подкачки? Я предполагаю, что у вас есть какой-то метод Take(50, 50)
, который получает 50 записей, начиная с записи 50. Взгляните на SQL, который генерирует запрос, и вы, вероятно, обнаружите, что он использует аналогичную структуру подзапроса, чтобы позволить ей возвращать любые 50 строк в запросе примерно в течение времени, в течение которого он возвращает первые 50 строк.
В любом случае вложенный дочерний запрос не добавляет никаких служебных накладных расходов, поскольку он автоматически оптимизируется во время компиляции плана выполнения.
Ответ 3
Подзапрос создается для целей проецирования, имеет смысл, когда вы выбираете из нескольких таблиц в один анонимный объект, тогда внешний запрос используется для сбора результатов.
Попробуйте что-то вроде этого:
from book in books
where price > 50
select new
{
Title = book.title,
Chapters = from chapter in book.Chapters
select chapter.Title
}
Ответ 4
Разве это не случай первого запроса, возвращающего общее количество строк, а второй извлекает подмножество строк на основе вызова метода .Take()?
Ответ 5
- Я согласен с @Justin Swartsel. Не было ошибки, так что это в значительной степени академический вопрос.
- Linq-to-SQL стремится генерировать SQL, который работает эффективно (что было в вашем случае).
- Но он не прилагает никаких усилий для создания обычного SQL, который, вероятно, создаст человек.
- У реалистов Linq-to-SQL, вероятно, использовался шаблон построителя для генерации SQL.
- Если это так, было бы проще добавить подстроку (или подзапрос в этом случае), чем было бы возвращать назад и вставить фрагмент "TOP x" в предложение SELECT.