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


172

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

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


В SQL Server 2012 эта функция реализована простым способом. Смотри мой ответ
Сомнат Мулук

Спасибо, что задали этот вопрос, мы вынуждены перейти с MySQL на MsSQL :(
tempcke

Вы можете использовать смещение и извлекать следующий оператор на SQL-сервере с предложением order by. Попробуй youtu.be/EqHkAiiBwPc
Амреш Кумар Сингх

Ответы:


139

Эквивалент 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 параметр следует использовать одну индексирование для этого , а не нормальной индексации с нуля.


22
Старый сейчас. Sql Server 2012 и более поздние
версии

31
@JoelCoehoorn Не старый. Я только что получил назначение на проект с использованием SLQ Server 2008, который использовал только mysql в прошлом ...
Ктулху,

Это довольно хорошо, но нужно немного подкорректироватьWHERE RowNum >= (@Offset + 1)
Эрик Херлиц,

5
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.
Пол

2
@Aaronaught Если у моего Tableесть 200 тыс. Записей, сначала он получит все, а затем установит лимит? Этот запрос эффективен?
Джигар

231

Эта функция теперь упрощена в 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;
  • OFFSET: количество пропущенных строк
  • NEXT: необходимое количество следующих строк

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


4
Есть ли эквивалент SQL_CALC_FOUND_ROWSпри использовании этого?
Петах

1
@Petah @@ Rowcount даст вам то, что я думаю
Роб Седжвик

GOTCHA: Вы не можете использовать это из CTE. Это должно быть использовано в основном запросе. Я хотел ограничить количество возвращаемых строк (разбивка на страницы), а затем выполнить дорогостоящий расчет до примерно 10 возвращенных строк, а не определять строки, выполнить дорогостоящее вычисление, а затем пропустить / взять то, что мне было нужно. @ Ответ Аарона будет работать для тех, кому нужно ограничить строки в CTE.
Дерек Дин

@Somnath Muluk Это смещение и выборка занимают много времени для примера с большим объемом данных со смещением 1000000. Как с этим справиться.
Сародж Шреста

1
@SarojShrestha: Это не проблема смещения и извлечения. Вы должны пересмотреть архитектуру вашего стола сейчас. Рассмотрите возможность секционирования таблиц, строки данных и разных типов столбцов и общего размера таблицы, рассмотрите возможность архивирования некоторых строк, если это не требуется регулярно, проверьте спецификации сервера.
Сомнат Мулук,

23
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()было реализовано.


Я использовал этот запрос некоторое время, и он прекрасно работает, так что спасибо за это. Мне просто интересно, что представляет «хх»?
Урбли

Подзапрос требует имя. поскольку я не использую это просто поместите xx там
jorgeu

2
Хх это просто псевдоним таблицы. Это может быть немного яснее, если вы скажетеAS xx
Бетонные олуша

Кто-нибудь знает, как сделать левое соединение по этому запросу?
Дренил

12

Вы можете использовать ROW_NUMBER в выражении Common Table для достижения этой цели.

;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

4

Для меня использование 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 для меня не сработало.


1
Я получаю «TOP не может быть использован в том же запросе или подзапросе, что и OFFSET».
MichaelRushton

Вы правы @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. Я отредактирую свой ответ. Спасибо и извините за мой английский.
Себадев

2

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

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;

15
Я удалил вашу ненавистническую речь против Microsoft. Не обсуждайте священные войны здесь; просто отвечайте и задавайте вопросы не субъективно.
Эрлз

2

Существует здесь кто - то говорил об этой функции в SQL 2011, его печально они выбирают немного другое ключевое слово «OFFSET / FETCH» , но его не стандарт , то нормально.


2

Добавляя небольшую вариацию решения Аарона, я обычно параметризую номер страницы (@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

2

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

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


2
-- @RowsPerPage  can be a fixed number and @PageNumber number can be passed 
DECLARE @RowsPerPage INT = 10, @PageNumber INT = 2

SELECT *

FROM MemberEmployeeData

ORDER BY EmployeeNumber

OFFSET @PageNumber*@RowsPerPage ROWS

FETCH NEXT 10 ROWS ONLY

Работает для Microsoft SQL Server 13.x Большое спасибо.
Шубхам Арья

1
select top (@TakeCount) * --FETCH NEXT
from(
    Select  ROW_NUMBER() OVER (order by StartDate) AS rowid,*
    From YourTable
)A
where Rowid>@SkipCount --OFFSET

1
@nombre_row :nombre ligne par page  
@page:numero de la page

//--------------code sql---------------

declare  @page int,@nombre_row int;
    set @page='2';
    set @nombre_row=5;
    SELECT  *
FROM    ( SELECT    ROW_NUMBER() OVER ( ORDER BY etudiant_ID ) AS RowNum, *
      FROM      etudiant

    ) AS RowConstrainedResult
WHERE   RowNum >= ((@page-1)*@nombre_row)+1
    AND RowNum < ((@page)*@nombre_row)+1
ORDER BY RowNum

1

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

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есть ли явно, если это то, что нужно

1

Специально для 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: с "OFFSET and FETCH", но с этим "ORDER BY" является обязательным

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

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



-1

С тех пор, как я тестирую больше раз, этот скрипт более полезен на 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}: количество пропущенных записей

2
Хотя этот код может решить вопрос, в том числе объяснение того, как и почему это решает проблему, действительно поможет улучшить качество вашего сообщения и, вероятно, получит больше голосов "за". Помните, что вы отвечаете на вопрос для читателей в будущем, а не только для того, кто спрашивает сейчас. Пожалуйста, отредактируйте свой ответ, чтобы добавить объяснения и указать, какие ограничения и предположения применяются.
Брайан
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.