Как мне написать LINQ's .Skip (1000) .Take (100) на чистом SQL?

93

Что представляет собой SQL-эквивалент .Skip()метода в LINQ?

Например: я хотел бы выбрать строки 1000-1100 из определенной таблицы базы данных.

Возможно ли это с помощью только SQL? Или мне нужно выделить всю таблицу, а затем найти строки в памяти? В идеале я бы хотел по возможности избежать этого, так как стол может быть довольно большим.

Луч
источник

Ответы:

78

В SQL Server 2005 и выше вы можете использовать функцию ROW_NUMBER . например.

USE AdventureWorks;
GO
WITH OrderedOrders AS
(
    SELECT SalesOrderID, OrderDate,
    ROW_NUMBER() OVER (ORDER BY OrderDate) AS 'RowNumber'
    FROM Sales.SalesOrderHeader 
) 
SELECT * 
FROM OrderedOrders 
WHERE RowNumber BETWEEN 51 AND 60; --BETWEEN is inclusive
Дэн Дипло
источник
См. Ссылку в моем ответе для более подробной информации. stackoverflow.com/questions/1744802/…
Майк Атлас
МЕЖДУ 51 и 60 - включительно.
Дрю Миллер
1
Но это сначала выберет все, а затем из этого выбора займет только 10, верно? Или в первом запросе / просмотре уже будет только 10?
Tadej
139

В SQL Server 2012 и выше добавлен следующий синтаксис:

SELECT *
FROM Sales.SalesOrderHeader 
ORDER BY OrderDate
OFFSET (@Skip) ROWS FETCH NEXT (@Take) ROWS ONLY
Джон Гитцен
источник
11
Обратите внимание, что вам нужно использовать ORDER BY ___, чтобы использовать команду OFFSET .... не то чтобы вам когда-либо следовало пытаться разбивать на страницы без заказа.
Джеймс Хауг
Также обратите внимание на то, что «новый» синтаксис, как ни странно, имеет линейное снижение производительности с @skip! Подход row_number НЕ имеет этого (проверено только в индексированном порядке). Однако для lo @Skip меньше 20 новый синтаксис быстрее, чем подход row_number.
Эске Ран
22

LINQ to SQL делает это с помощью оконной функции ROW_NUMBER:

  SELECT a,b,c FROM 
   (SELECT a,b,c, ROW_NUMBER() OVER (ORDER BY ...) as row_number
    FROM Table) t0
   WHERE to.row_number BETWEEN 1000 and 1100;

Это работает, но необходимость создания row_number из ORDER BY может привести к тому, что ваш запрос будет отсортирован на стороне сервера и вызовет проблемы с производительностью. Даже если индекс может удовлетворить требование ORDER BY, запрос все равно должен подсчитать 1000 строк, прежде чем он начнет возвращать результаты. Слишком часто разработчики забывают об этом и просто вводят элемент управления разбиением на страницы для таблицы в 5 миллионов строк и задаются вопросом, почему первая страница возвращается намного быстрее, чем последняя ...

Тем не менее использование ROW_NUMBER (), вероятно, является лучшим балансом между простотой использования и хорошей производительностью, при условии, что вы избегаете сортировки (условие ORDER BY может быть выполнено с помощью индекса).

Ремус Русану
источник
1
Спасибо за дополнительную информацию о производительности, будьте осторожны и протестируйте ее.
Ray
Протестировано, и для моей таблицы с полмиллионом строк эта последняя страница примерно в 7 раз медленнее первой. Не идеально, но для меня приемлемо.
Ray
6

Попробуй это:

select * from [Table-Name] order by [Column-Name] 
offset [Skip-Count] rows
FETCH NEXT [Take-Count] rows only

Пример:

select * from Personals order by Id
offset 10 rows            --------->Skip 10
FETCH NEXT 15 rows only   --------->Take 15
Ферейдун Барикзехи
источник
4

Сделай это:

Запустите .Skip (1000) .Take (100) в тексте данных LINQ to SQL и просмотрите выходные данные SQL. Он сгенерирует для вас оператор SQL, который выполняет то, что вы описываете.

Он не будет таким элегантным, но выполнит свою работу.

Джозеф
источник
2
Не то, о чем просили.
RayLoveless
2

Нет, но вы можете эмулировать предложение MySQL LIMIT (ссылка на переполнение стека) для достижения того же результата.

Майк Атлас
источник
1
Принятый ответ указывает на интересную ссылку CodeProject «Разбиение на страницы больших наборов результатов в ASP.NET» (более ориентированной на SQL, чем следует из названия).
ruffin