Эквивалент LIMIT и OFFSET для SQL Server?

В PostgreSQL есть ключевые слова Limit и Offset, которые позволяют очень легко разбивать наборы результатов на страницы.

Каков эквивалентный синтаксис для SQL Server?


person Earlz    schedule 25.01.2010    source источник
comment
Для sql server 2012 эта функция реализована очень просто. См. мой ответ   -  person Somnath Muluk    schedule 11.01.2013
comment
Спасибо, что задали этот вопрос, нас заставляют перейти с MySQL на MsSQL :(   -  person tempcke    schedule 05.11.2015
comment
Вы можете использовать оператор offset и fetch next на сервере SQL с предложением order by. Попробуйте youtu.be/EqHkAiiBwPc   -  person Amresh Kumar Singh    schedule 17.05.2020
comment
OFFSET / FETCH in ORDER CLAUSE - это стандарт SQL ISO. LIMIT и TOP являются решениями поставщиков и не переносятся между разными СУБД.   -  person SQLpro    schedule 07.10.2020


Ответы (15)


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

Преимуществом здесь является параметризация смещения и ограничения на случай, если вы решите изменить параметры разбиения на страницы (или разрешите это сделать пользователю).

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

person Aaronaught    schedule 25.01.2010
comment
Сейчас старый. Sql Server 2012 и более поздние версии поддерживают смещение / выборку - person Joel Coehoorn; 17.09.2013
comment
@JoelCoehoorn Не старый. Меня только что назначили для проекта с использованием SLQ Server 2008, в прошлом я использовал только mysql ... - person Cthulhu; 13.02.2014
comment
Это неплохо, но нужно немного подкорректировать WHERE RowNum >= (@Offset + 1) - person Eric Herlitz; 03.04.2014
comment
@EricHerlitz: См. Примечание в конце этого ответа. - person Aaronaught; 04.04.2014
comment
The ORDER BY clause is invalid in views, inline functions, derived tables, subqueries, and common table expressions, unless TOP or FOR XML is also specified. MSSQL2008 R2. - person Paul; 26.08.2014
comment
К сожалению, заказ не будет работать из-за СО. - person Zorkind; 28.08.2015
comment
@EricHerlitz, не усложняйте это еще больше ... просто используйте WHERE RowNum > @Offset, и мы в порядке с условием запуска. - person Damian Vogel; 12.01.2016
comment
@Aaronaught Если мой Table имеет 200 тыс. Записей, он сначала извлечет все, а затем применит лимит? Этот запрос эффективен? - person Jigar; 20.06.2016
comment
кто-нибудь знает, как сделать left join по этому запросу? - person Drenyl; 22.02.2019
comment
RowNum ›= (@ Offset + 1) AND RowNum‹ (@ Offset + 1) + @Limit - person Amin Golmahalle; 28.02.2019
comment
Я сравнил результаты этого запроса с SELECT * FROM Table WHERE ‹whatever› ORDER BY (SortCol1, SortCol2, ...) ASC OFFSET * Offset ROWS FETCH NEXT * Limit ROWS ONLY; И оба, похоже, работают одинаково с одинаковой эффективностью. Предоставляет ли этот запрос какие-либо дополнительные преимущества? Также следует отметить, что эффективность обоих запросов начинает падать при увеличении * Offset. @Jigar - person Anupam Chand; 29.04.2021

Эта функция теперь упрощена в SQL Server 2012. Это работает с SQL Server 2012 и далее.

Ограничьте со смещением, чтобы выбрать от 11 до 20 строк в SQL Server:

SELECT email FROM emailTable 
WHERE user_id=3
ORDER BY Id
OFFSET 10 ROWS
FETCH NEXT 10 ROWS ONLY;
  • ORDER BY: требуется
  • OFFSET: необязательное количество пропущенных строк
  • NEXT: необходимое количество следующих строк

Ссылка: https://docs.microsoft.com/en-us/sql/t-sql/queries/select-order-by-clause-transact-sql

person Somnath Muluk    schedule 13.02.2012
comment
Есть ли эквивалент SQL_CALC_FOUND_ROWS при использовании этого? - person Petah; 24.03.2015
comment
@Petah @@ Rowcount даст вам то, что я думаю - person Rob Sedgwick; 20.12.2015
comment
GOTCHA: Вы не можете использовать это из CTE. Его нужно использовать в основном запросе. Я хотел ограничить количество возвращаемых строк (разбиение на страницы), а затем выполнить дорогостоящие вычисления до 10 или около того возвращаемых строк, вместо того, чтобы определять строки, выполнять дорогостоящие вычисления, а затем пропускать / брать то, что мне нужно. Ответ @Aaronaught будет работать для тех, кому нужно ограничить строки в CTE. - person Derreck Dean; 19.06.2017
comment
@SarojShrestha: это не проблема смещения и извлечения. Теперь вам следует вернуться к архитектуре вашего стола. Рассмотрите возможность секционирования таблиц, вашей строки данных, различных типов столбцов и общего размера таблицы, рассмотрите возможность архивирования некоторых строк, если это не требуется регулярно, проверьте спецификации вашего сервера. - person Somnath Muluk; 28.10.2018
comment
Большое спасибо @SomnathMuluk - person mickael; 17.10.2020

select top {LIMIT HERE} * from (
      select *, ROW_NUMBER() over (order by {ORDER FIELD}) as r_n_n 
      from {YOUR TABLES} where {OTHER OPTIONAL FILTERS}
) xx where r_n_n >={OFFSET HERE}

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

person jorgeu    schedule 26.03.2013
comment
Я использую этот запрос уже некоторое время, и он отлично работает, так что спасибо за это. Мне просто интересно, что означает «xx»? - person Urbley; 21.03.2014
comment
для подзапроса требуется имя. так как я не использую его, просто положите xx туда - person jorgeu; 24.03.2014
comment
Xx - это просто псевдоним таблицы. Было бы немного яснее, если бы вы сказали AS xx - person Concrete Gannet; 05.04.2016
comment
кто-нибудь знает, как сделать левое соединение по этому запросу? - person Drenyl; 22.02.2019

Для этого вы можете использовать ROW_NUMBER в общем табличном выражении.

;WITH My_CTE AS
(
     SELECT
          col1,
          col2,
          ROW_NUMBER() OVER(ORDER BY col1) AS row_number
     FROM
          My_Table
     WHERE
          <<<whatever>>>
)
SELECT
     col1,
     col2
FROM
     My_CTE
WHERE
     row_number BETWEEN @start_row AND @end_row
person Tom H    schedule 25.01.2010

Для меня использование OFFSET и FETCH вместе было медленным, поэтому я использовал комбинацию TOP и OFFSET, подобную этой (которая была быстрее):

SELECT TOP 20 * FROM (SELECT columname1, columname2 FROM tablename
    WHERE <conditions...> ORDER BY columname1 OFFSET 100 ROWS) aliasname

Примечание. Если вы используете TOP и OFFSET вместе в одном запросе, например:

SELECT TOP 20 columname1, columname2 FROM tablename
    WHERE <conditions...> ORDER BY columname1 OFFSET 100 ROWS

Затем вы получите сообщение об ошибке, поэтому для совместного использования TOP и OFFSET вам необходимо разделить его подзапросом.

И если вам нужно использовать SELECT DISTINCT, тогда запрос будет примерно таким:

SELECT TOP 20 FROM (SELECT DISTINCT columname1, columname2
    WHERE <conditions...> ORDER BY columname1 OFFSET 100 ROWS) aliasname

Примечание. Использование SELECT ROW_NUMBER с DISTINCT не помогло мне.

person sebasdev    schedule 22.02.2017
comment
Я получаю, что ТОП нельзя использовать в том же запросе или подзапросе, что и СМЕЩЕНИЕ. - person MichaelRushton; 08.05.2017
comment
Вы правы, @MichaelRushton, не может использоваться в том же запросе или в одном подзапросе, тогда вам нужно использовать подзапрос, чтобы разделить его. Итак, если у вас есть SQL типа SELECT TOP 20 id FROM table1 where id > 10 order by date OFFSET 20 rows, вы должны преобразовать его, как SELECT TOP 20 * FROM (SELECT id FROM table1 where id > 10 order by date OFFSET 20 ROWS) t1. Отредактирую свой ответ. Спасибо и извините за мой английский. - person sebasdev; 09.05.2017

Добавляя небольшое изменение к решению Aaronaught, я обычно параметризую номер страницы (@PageNum) и размер страницы (@PageSize). Таким образом, каждое событие нажатия страницы просто отправляет запрошенный номер страницы вместе с настраиваемым размером страницы:

begin
    with My_CTE  as
    (
         SELECT col1,
              ROW_NUMBER() OVER(ORDER BY col1) AS row_number
     FROM
          My_Table
     WHERE
          <<<whatever>>>
    )
    select * from My_CTE
            WHERE RowNum BETWEEN (@PageNum - 1) * (@PageSize + 1) 
                              AND @PageNum * @PageSize

end
person Tom    schedule 26.02.2013

В частности, для SQL-SERVER это можно сделать разными способами. В качестве реального примера мы взяли здесь таблицу Customer.

Пример 1. С SET ROWCOUNT

SET ROWCOUNT 10
SELECT CustomerID, CompanyName from Customers
ORDER BY CompanyName

Чтобы вернуть все строки, установите для ROWCOUNT значение 0.

SET ROWCOUNT 0  
SELECT CustomerID, CompanyName from Customers
    ORDER BY CompanyName

Пример 2: с «ROW_NUMBER and OVER»

With Cust AS
( SELECT CustomerID, CompanyName,
ROW_NUMBER() OVER (order by CompanyName) as RowNumber 
FROM Customers )
select *
from Cust
Where RowNumber Between 0 and 10

Пример 3: с "СМЕЩЕНИЕМ и ПОЛУЧЕНИЕМ", но с этим "ЗАКАЗАТЬ ПО" является обязательным

SELECT CustomerID, CompanyName FROM Customers
ORDER BY CompanyName
OFFSET 0 ROWS
FETCH NEXT 10 ROWS ONLY

Надеюсь, это тебе поможет.

person Humayoun_Kabir    schedule 26.12.2019

Другой образец:

declare @limit int 
declare @offset int 
set @offset = 2;
set @limit = 20;
declare @count int
declare @idxini int 
declare @idxfim int 
select @idxfim = @offset * @limit
select @idxini = @idxfim - (@limit-1);
WITH paging AS
    (
        SELECT 
             ROW_NUMBER() OVER (order by object_id) AS rowid, *
        FROM 
            sys.objects 
    )
select *
    from 
        (select COUNT(1) as rowqtd from paging) qtd, 
            paging 
    where 
        rowid between @idxini and @idxfim
    order by 
        rowid;
person sillyim    schedule 06.07.2011
comment
Я удалил ваш язык ненависти, направленный против Microsoft. Не обсуждайте здесь священные войны; просто отвечайте и задавайте вопросы без субъективности. - person Earlz; 07.07.2011

здесь кто-то рассказывает о эта функция в sql 2011, грустно, что они выбрали немного другое ключевое слово «OFFSET / FETCH», но это не стандартно, тогда хорошо.

person keepkeywordspleeeease    schedule 17.08.2011

Самое близкое, что я мог сделать, это

select * FROM( SELECT *, ROW_NUMBER() over (ORDER BY ID ) as ct from [db].[dbo].[table] ) sub where ct > fromNumber  and ct <= toNumber

Что, я думаю, похоже на select * from [db].[dbo].[table] LIMIT 0, 10

person user2991730    schedule 14.11.2013

Поскольку еще никто не предоставил этот код:

SELECT TOP @limit f1, f2, f3...
FROM t1
WHERE c1 = v1, c2 > v2...
AND
    t1.id NOT IN
        (SELECT TOP @offset id
         FROM t1
         WHERE c1 = v1, c2 > v2...
         ORDER BY o1, o2...)
ORDER BY o1, o2...

Важные точки:

  • ORDER BY должен быть идентичным
  • @limit можно заменить на количество результатов, которые нужно получить,
  • @offset - количество результатов, которые нужно пропустить
  • Пожалуйста, сравните производительность с предыдущими решениями, так как они могут быть более эффективными
  • это решение дублирует предложения where и order by и даст неверные результаты, если они не синхронизированы
  • с другой стороны order by явно присутствует, если это то, что нужно
person przemo_li    schedule 19.09.2016

В SQL-сервере вы должны использовать TOP вместе с ROW_NUMBER ()

person SQLMenace    schedule 25.01.2010

Поскольку я тестирую этот сценарий больше раз, более полезный на 1 миллион записей на каждую страницу. 100 записей с разбивкой на страницы работают быстрее, мой компьютер выполняет этот сценарий 0 секунд, в то время как по сравнению с mysql имеет собственный предел и смещение около 4,5 секунд для получения результата.

Кто-то может не понимать, что Row_Number () всегда сортируется по определенному полю. В случае, если нам нужно определить только строку в последовательности, следует использовать:

ROW_NUMBER () OVER (ORDER BY (SELECT NULL))

SELECT TOP {LIMIT} * FROM (
      SELECT TOP {LIMIT} + {OFFSET} ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS ROW_NO,*
      FROM  {TABLE_NAME}
) XX WHERE ROW_NO > {OFFSET}

Объяснять:

  • {LIMIT}: количество записей для каждой страницы.
  • {OFFSET}: количество пропущенных записей.
person Vanda Ros    schedule 12.04.2020
comment
Хотя этот код может решить вопрос, включая объяснение того, как и почему это решает проблему, действительно поможет улучшить качество. вашего сообщения, и, вероятно, приведет к большему количеству голосов. Помните, что вы отвечаете на вопрос для будущих читателей, а не только для человека, который задает его сейчас. Пожалуйста, отредактируйте свой ответ, чтобы добавить пояснения и указать, какие ограничения и предположения применяются. - person Brian; 12.04.2020

person    schedule
comment
Работает для Microsoft SQL Server 13.x Большое спасибо. - person Shubham Arya; 20.07.2020

person    schedule
comment
ROW_NUMBER () OVER - медленный процесс получения результатов ... - person Monzur; 29.04.2021