LIMIT 10..20 в SQL Server

Я пытаюсь сделать что-то вроде:

SELECT * FROM table LIMIT 10,20

or

SELECT * FROM table LIMIT 10 OFFSET 10

но используя SQL Server

Единственное решение, которое я нашел похоже на перебор:

SELECT * FROM ( 
  SELECT *, ROW_NUMBER() OVER (ORDER BY name) as row FROM sys.databases 
 ) a WHERE row > 5 and row <= 10

Я также обнаружил:

SELECT TOP 10 * FROM stuff; 

... но это не то, что я хочу делать, поскольку я не могу указать начальный предел.

Есть ли у меня другой способ сделать это?

Кроме того, просто любопытно, есть ли причина, по которой SQL Server не поддерживает функцию LIMIT или что-то подобное? Не хочу показаться злым, но это действительно похоже на то, что СУБД нужно ... Если это так, то извините за такое невежество! Я работаю с MySQL и SQL + последние 5 лет, так что ...


person marcgg    schedule 09.06.2009    source источник
comment
Использование CTE для ROW_NUMBER() и ограничение TOP для ширины диапазона и условия WHERE для границы диапазона - лучшее, что мне удалось достичь. Я также заметил гораздо лучшую производительность, если в предложении TOP вместо переменной используется литерал.   -  person Jodrell    schedule 04.07.2012
comment
Проблема с любым решением, включающим ROW_NUMBER (), заключается в том, что если вы заранее не знаете, какие столбцы у вас будут, и у вас есть объединения, а у соединенных таблиц одинаковое имя столбца, вы получите столбец The column ' xxx 'был указан несколько раз. Это не так уж редко, как может показаться на первый взгляд. Я использую Dapper, и все мои таблицы имеют столбец Id. Dapper разделяет и сопоставляет их, поэтому я не хочу их переименовывать, но я не могу использовать псевдоним SELECT * FROM ([исходный запрос]). Пока не придумал решения!   -  person Steve Owen    schedule 31.01.2014
comment
Возможный дубликат Как реализовать LIMIT с Microsoft SQL Server?   -  person kenorb    schedule 08.02.2018


Ответы (15)


Предложение LIMIT не является частью стандартного SQL. Он поддерживается как расширение поставщика для SQL в MySQL, PostgreSQL и SQLite.

Базы данных других производителей могут иметь аналогичные функции (например, TOP в Microsoft SQL Server), но они не всегда работают одинаково.

Трудно использовать TOP в Microsoft SQL Server для имитации предложения LIMIT. Бывают случаи, когда это просто не работает.

Показанное вами решение с использованием ROW_NUMBER() доступно в Microsoft SQL Server 2005 и более поздних версиях. Это лучшее решение (на данный момент), которое работает исключительно как часть запроса.

Другое решение - использовать TOP для выборки первых строк count + offset, а затем использовать API для поиска мимо первых строк offset.

Смотрите также:

person Bill Karwin    schedule 09.06.2009

Для SQL Server 2012 + можно использовать.

SELECT  *
FROM     sys.databases
ORDER BY name 
OFFSET  5 ROWS 
FETCH NEXT 5 ROWS ONLY 
person Martin Smith    schedule 11.02.2012
comment
SQl Server 2012 требует указывать ORDER BY при использовании OFFSET 5 ROWS FETCH NEXT 5 ROWS ONLY, в то время как MySql и SQLite не требуют ORDER BY при использовании LIMIT 5,5 - person Tomas Kubes; 07.08.2013
comment
@ qub1n - MySQL не гарантирует, какие строки в этом случае вы вернетесь. - person Martin Smith; 07.08.2013
comment
Вам нужно использовать offset, или вы можете оставить эту строку (при условии, что вам не нужно смещение)? - person Cullub; 27.01.2016
comment
comment
Ваш пример запроса работает нормально, но если я изменю имя таблицы и порядок по столбцу, как показано ниже SELECT * FROM DimProduct ORDER BY ProductKey OFFSET 5 ROWS FETCH NEXT 5 ROWS ONLY, это дает ошибку Parse error at line: 4, column: 1: Incorrect syntax near 'OFFSET' - person shashwat; 12.09.2018
comment
@shashwat - похоже, что вы используете версию SQL Server, которая не поддерживает этот (или уровень совместимости с базой данных). Это введено в 2012 году. - person Martin Smith; 12.09.2018
comment
@MartinSmith SELECT compatibility_level FROM sys.databases WHERE name = 'MyDbName' возвращает 130, что означает SQL Server 2016. Это экземпляр SQL Server DW в Azure, если он отличается. - person shashwat; 12.09.2018
comment
@MartinSmith FYI вы действительно получаете предсказуемые результаты без заказа, потому что механизм запросов MySQL сканирует таблицы на основе точки входа. Единственное, чего вы не можете знать, это то, что если ваш лимит превышает известный размер существующей таблицы (т.е. захват новых / горячих вставок), вы не можете знать, что получите. Однако, если вы попадете в одну и ту же таблицу с несортированным предложением LIMIT x, x, вы будете каждый раз получать одни и те же записи. Это может напасть на вас из-за плохих узлов репликации ... - person patrickgamer; 15.10.2018

как вы обнаружили, это предпочтительный метод sql server:

SELECT * FROM ( 
  SELECT *, ROW_NUMBER() OVER (ORDER BY name) as row FROM sys.databases 
 ) a WHERE a.row > 5 and a.row <= 10
person KM.    schedule 09.06.2009
comment
Почему a после внутреннего выбора? Я предполагаю, что вы даете внутреннему select псевдоним, но тогда вы, кажется, никогда его не используете ... Должны ли вы тогда сделать a.row вместо просто row? - person Lucas; 24.09.2012
comment
@Lucas, вы должны указать псевдоним после ( ) производной таблицы, но он отпустит его, если вы затем забудете использовать его для ссылки на столбцы. Но я исправил ... - person KM.; 25.09.2012
comment
спасибо, я обнаружил это на собственном горьком опыте (пытался не указывать псевдоним). - person Lucas; 25.09.2012
comment
Проголосовали +1: Однако за ответ @MartinSmith проголосовало больше, после сравнения плана выполнения с этим подходом я узнал что это решение работает намного быстрее. - person Harsh; 31.12.2018

Если вы используете SQL Server 2012+, голосуйте за ответ Мартина Смита и используйте расширения OFFSET и FETCH NEXT для ORDER BY,

Если вам не повезло, что вы застряли на более ранней версии, вы можете сделать что-то вроде этого:

WITH Rows AS
(
    SELECT
              ROW_NUMBER() OVER (ORDER BY [dbo].[SomeColumn]) [Row]
            , *
        FROM
              [dbo].[SomeTable]
)
SELECT TOP 10
          *
     FROM
         Rows
    WHERE Row > 10

Я считаю, что функционально эквивалентен

SELECT * FROM SomeTable LIMIT 10 OFFSET 10 ORDER BY SomeColumn

и лучший из известных мне способов сделать это в TSQL до MS SQL 2012.


Если строк очень много, вы можете повысить производительность, используя временную таблицу вместо CTE.

person Jodrell    schedule 04.07.2012
comment
Проголосовали за указание на ответ Мартина Смита (и ссылку на него) при предоставлении решения до 2012 года. Также за совет по временной таблице, потому что вы правы :) - person fujiiface; 05.04.2017

Как насчет этого?

SET ROWCOUNT 10 

SELECT TOP 20 *
FROM sys.databases
ORDER BY database_id DESC

Это дает вам последние 10 рядов первых 20 рядов. Одним из недостатков является обратный порядок, но, по крайней мере, его легко запомнить.

person David Patrick    schedule 09.06.2009
comment
Что делать, если в таблице всего 14 строк? Вы получаете строки с 14 по 5, что не то же самое, что строки, возвращаемые LIMIT 10 OFFSET 10 (должны быть строки с 14 по 11). - person Bill Karwin; 09.06.2009

К сожалению, ROW_NUMBER() - лучшее, что вы можете сделать. На самом деле это более правильно, потому что результаты предложения limit или top на самом деле не имеют значения без учета определенного порядка. Но это все еще боль.

Обновление. Sql Server 2012 добавляет limit -подобную функцию через Ключевые слова OFFSET и FETCH. Это стандартный подход ansi, в отличие от LIMIT, который является нестандартным расширением MySql.

person Joel Coehoorn    schedule 09.06.2009
comment
@Joel: Можете ли вы объяснить, почему ROW_NUMBER () не может пронумеровать строки так, как они выходят из ORDER BY? Я всегда задавался вопросом, почему OVER (ORDER BY name) является обязательным, но я думаю, для этого есть веская причина. Или, по крайней мере, причина. - person Tomalak; 09.06.2009
comment
потому что не существует такой вещи, как порядок, без пункта заказа по пункту. Вы получаете любой порядок, в котором записи были доступны серверу, и он мог изменяться от запроса к запросу запроса. - person Joel Coehoorn; 09.06.2009
comment
Но я явно имел в виду предложение ORDER BY. Предположим, что он есть в самом запросе - почему ROW_NUMBER () не может только этого? Я имею в виду, какая внутренняя причина БД заставляет меня изменять запрос в двух местах, если я хочу получить его часть в другом порядке? - person Tomalak; 10.06.2009
comment
Интересный. Знаете ли вы, планирует ли Microsoft включить функцию ограничения в будущий выпуск sqlserver? - person marcgg; 10.06.2009
comment
@marcgg: Я никогда не читал никаких указаний на то, что Microsoft планирует внедрить LIMIT. Даже если у них есть такой план, поставщики закрытых исходных кодов обычно не объявляют о функциях заранее. Это, безусловно, было бы полезной функцией, но мы не знаем, сколько работы придется реализовать, учитывая их код. - person Bill Karwin; 10.06.2009
comment
Если вы не хотите повторяться в предложении ORDER BY, используйте псевдоним ROW_NUMBER (), а не исходный набор столбцов. - person Peter Radocchia; 10.06.2009
comment
@Tomalak: Что касается SQL Server, порядок, используемый для вычисления ROW_NUMBER (), полностью не связан с порядком набора результатов. Поэтому их нужно указывать отдельно. - person LukeH; 10.06.2009

SELECT TOP 10 *
FROM TABLE
WHERE IDCOLUMN NOT IN (SELECT TOP 10 IDCOLUMN FROM TABLE)

Должен дать записи 11-20. Вероятно, не слишком эффективно при увеличении, чтобы получить дополнительные страницы, и не уверен, как на это может повлиять заказ. Возможно, придется указать это в обоих операторах WHERE.

person Andy    schedule 10.01.2011
comment
Вам всегда нужен ORDER BY, чтобы гарантировать заказ. У вас есть два TOP, поэтому вам нужно два ORDER BY - person Nick.McDermaid; 11.03.2021

Хороший способ - создать процедуру:

create proc pagination (@startfrom int ,@endto int) as
SELECT * FROM ( 
  SELECT *, ROW_NUMBER() OVER (ORDER BY name desc) as row FROM sys.databases 
 ) a WHERE a.row > @startfrom and a.row <= @endto

точно так же, как limit 0,2 /////////////// выполнить разбиение на страницы 0,4

person Wahaj Latif    schedule 28.11.2013

Просто для решения записи, которое работает с большинством движков баз данных, хотя может быть не самым эффективным:

Select Top (ReturnCount) *
From (
    Select Top (SkipCount + ReturnCount) *
    From SourceTable
    Order By ReverseSortCondition
) ReverseSorted
Order By SortCondition

Примечание Pelase: последняя страница все равно будет содержать строки ReturnCount независимо от того, что такое SkipCount. Но во многих случаях это может быть хорошо.

person Y.B.    schedule 01.03.2016

Эквивалент LIMIT - SET ROWCOUNT, но если вам нужна общая разбивка на страницы, лучше написать такой запрос:

;WITH Results_CTE AS
(
    SELECT
        Col1, Col2, ...,
        ROW_NUMBER() OVER (ORDER BY SortCol1, SortCol2, ...) AS RowNum
    FROM Table
    WHERE <whatever>
)
SELECT *
FROM Results_CTE
WHERE RowNum >= @Offset
AND RowNum < @Offset + @Limit
person Satish Kumar sonker    schedule 12.04.2016

select * from (select id,name,ROW_NUMBER() OVER (ORDER BY id  asc) as row
from tableName1) tbl1
where tbl1.row>=10 and tbl1.row<=15

Напечатает строки от 10 до 15.

person sjith    schedule 22.01.2013

Пока что у меня работает этот формат (хотя и не самая лучшая производительность):

SELECT TOP {desired amount of rows} * 
FROM (SELECT *, ROW_NUMBER() OVER (ORDER BY {order columns} asc)__row__ FROM {table})tmp
WHERE __row__ > {offset row count}

Примечание сбоку: разбивка на страницы динамических данных может привести к странным / неожиданным результатам.

person Charlie Affumigato    schedule 09.08.2013

Из интерактивной документации MS SQL Server (http://technet.microsoft.com/en-us/library/ms186734.aspx), вот их пример, который я протестировал и работает, для получения определенного набора строк. ROW_NUMBER требует OVER, но вы можете заказать все, что захотите:

WITH OrderedOrders AS
(
  SELECT SalesOrderID, OrderDate,
  ROW_NUMBER() OVER (ORDER BY OrderDate) AS RowNumber
  FROM Sales.SalesOrderHeader 
) 
SELECT SalesOrderID, OrderDate, RowNumber  
FROM OrderedOrders 
WHERE RowNumber BETWEEN 50 AND 60;
person Mandrake Button    schedule 08.10.2013

Использовать весь SQL-сервер:; с tbl как (SELECT ROW_NUMBER () over (order by (select 1)) as RowIndex, * from table) выбрать первые 10 * из tbl, где RowIndex> = 10

person Phạm Tấn Lợi    schedule 10.01.2015

 SELECT * FROM users WHERE Id Between 15 and 25

он будет печатать от 15 до 25 как предел в MYSQl

person SR1    schedule 05.06.2015
comment
Что, если пользователь удалил запись от 15 до 25? - person Gökçer Gökdal; 21.11.2015