LIMIT 10..20 в SQL Server


161

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

SELECT * FROM table LIMIT 10,20

или

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 лет, так что ...


1
Использование CTE для ROW_NUMBER()и ограничение TOPширины диапазона и WHEREусловия для границы диапазона - лучшее, чего я смог достичь. Я также заметил гораздо лучшую производительность, если в TOPпредложении вместо литерала используется литерал
Jodrell

Проблема с любым решением, включающим ROW_NUMBER (), состоит в том, что если вы заранее не знаете, какие столбцы у вас будут, и у вас есть объединения, и у объединяемых таблиц будет одинаковое имя столбца, вы получите «Столбец». 'xxx' был указан несколько раз ". Это не так необычно, как может показаться на первый взгляд. Я использую Dapper, и все мои таблицы имеют столбец Id. Даппер разбивает и отображает их, поэтому я не хочу их переименовывать, но я не могу использовать псевдоним SELECT * FROM ([оригинальный запрос]). Я еще не нашел решение!
Стив Оуэн

Ответы:


104

Предложение 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 для поиска за первыми строками смещения .

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


135

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

SELECT  *
FROM     sys.databases
ORDER BY name 
OFFSET  5 ROWS 
FETCH NEXT 5 ROWS ONLY 

10
SQl Server 2012 требует указывать ORDER BY при использовании OFFSET 5 ROWS, FETCH NEXT 5 ROWS ONLY, тогда как MySql и SQLite не требуют ORDER BY при использовании LIMIT 5,5
Томас Кубес

4
@ qub1n - MySQL не гарантирует, какие строки вы получите в этом случае.
Мартин Смит

3
Вы должны использовать offsetили можете оставить эту строку вне (при условии, что вы не хотите смещение)?
Cullub


Ваш пример запроса работает нормально, но если я изменю имя таблицы и порядок по столбцам, как показано ниже, ВЫБРАТЬ * ОТ DimProduct ЗАКАЗАТЬ ПО ProductKey СМЕЩЕНИЕ 5 СТРОК ПОЛУЧИТЬ СЛЕДУЮЩИЙ 5 СТРОК ТОЛЬКО Это выдает ошибкуParse error at line: 4, column: 1: Incorrect syntax near 'OFFSET'
shashwat

36

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

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

Почему aпосле внутреннего выбора? Я предполагаю, что вы даете внутреннему псевдониму выбора, но тогда вы, кажется, никогда не используете его ... Должны ли вы тогда делать a.rowвместо просто row?
Лукас

3
@Lucas, вы должны поместить псевдоним после ( )производной таблицы, но он отпустит его, если вы потом забудете использовать его для ссылки на столбцы. Я все исправил ...
КМ.

спасибо, я нашел это трудным путем (попытался опустить псевдоним).
Лукас

1
Проголосовано +1: Однако за ответ @MartinSmith проголосовали больше, сравнив план выполнения с этим подходом, я обнаружил, что это решение работает намного быстрее.
Суровый

10

Если вы используете 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.


Проголосовал за указание ответа Мартина Смита (и ссылки на него) при предоставлении решения до 2012 года. Также для совета временного стола, потому что вы правы :)
fujiiface

7

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

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


@Joel: Можете ли вы объяснить, почему ROW_NUMBER () не может нумеровать строки так, как они выходят из ORDER BY? Я всегда задавался вопросом, почему «OVER (ORDER BY name)» является обязательным, но я думаю, что для этого есть веская причина. Или , по крайней мере , причина.
Томалак

3
потому что не существует такой вещи, как заказ без заказа по предложению. Вы получаете любой порядок, в котором записи были доступны серверу, и это может измениться от запроса к запросу.
Джоэл Коухорн

1
@marcgg: я никогда не читал никаких указаний на то, что Microsoft планирует внедрить LIMIT. Даже если у них есть такой план, поставщики с закрытыми исходными кодами обычно не анонсируют функции. Это, безусловно, будет полезной функцией, но мы не знаем, сколько работы потребуется для реализации, учитывая их код.
Билл Карвин

3
Если вы не хотите повторяться в предложении ORDER BY, используйте псевдоним ROW_NUMBER (), а не исходный набор столбцов.
Питер Радоккия

2
@Tomalak: Что касается SQL Server, порядок, используемый для вычисления ROW_NUMBER (), совершенно не связан с порядком набора результатов. Вот почему вы должны указать их отдельно.
ЛукиH

6

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

SET ROWCOUNT 10 

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

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


6
Что если в таблице всего 14 строк? Вы получаете строки от 14 до 5, что не совпадает со строками, возвращаемыми LIMIT 10 OFFSET 10 (должно быть от 14 до 11).
Билл Карвин

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

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


1

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

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

просто как предел 0,2 /////////////// выполнить нумерацию страниц 0,4


1

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

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

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


1

Эквивалентом 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

0
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.


0

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

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}

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


0

Из онлайновой документации по 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;

0

Использовать весь SQL-сервер:; при tbl как (SELECT ROW_NUMBER () over (упорядочить по (выберите 1)) как RowIndex, * из таблицы) выбрать топ 10 * из tbl, где RowIndex> = 10


-3
 SELECT * FROM users WHERE Id Between 15 and 25

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


2
Что если пользователь удалил запись между 15 и 25?
Гекчер Гекдал
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.