Еквівалент LIMIT та OFFSET для SQL Server?


172

У PostgreSQL є ключові слова Limitта Offsetключові слова, які дозволять легко простежувати набори результатів.

Який еквівалентний синтаксис для SQL Server?


Для сервера sql 2012 ця функція реалізована просто. Дивіться мою відповідь
Сомнат Мулук

Дякуємо, що задали це запитання, ми змушені переходити з MySQL до MsSQL :(
tempcke

Ви можете використовувати зміщення та отримання наступного оператора на SQL-сервері з порядком за пунктом. Спробуйте 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 та новіших версій OFFSET / FETCH
Joel Coehoorn,

31
@JoelCoehoorn Не старий. Мені щойно приписали проект, використовуючи SLQ Server 2008, використовуючи тільки mysql в минулому ...
Cthulhu

Це досить добре, але його потрібно трохи відрегулювати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є 200k записів, вона спочатку отримає все, а потім застосувати ліміт? Цей запит ефективний?
Джигар

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 повернених рядків, а не визначати рядки, виконувати дорогі обчислення, а потім пропустити / взяти те, що мені потрібно. @ Відповідь Aaronaught допоможе тим, хто потребує обмеження рядків у межах 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"?
Урблей

підзапит вимагає імені. тому що я не використовую його, просто покладіть туди xx
jorgeu

2
Xx - це лише псевдонім таблиці. Можливо, буде трохи зрозуміліше, якщо ви сказалиAS xx
Concrete Gannet

хтось знає, як зробити лівий приєднання до цього запиту?
Дренил

12

Для цього можна використовувати 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

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
Я отримую "ТОП не можна використовувати в тому ж запиті чи підзапиті, що і OFFSET."
МайклРуштон

Ви маєте рацію @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
Я видалив вашу мову ненависті проти мікрософт. Тут не обговорюйте святих воєн; просто відповідайте і задавайте питання не суб'єктивно.
Граф

2

Існує тут хто - то говорив про цю функцію в SQL 2011, його сумно вони вибирають трохи інше ключове слово «OFFSET / FETCH» , але його не стандарт , то нормально.


2

Додаючи незначні зміни до рішення 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

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


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

Важливі моменти:

  • ЗАМОВЛЕННЯ ДО БУДЬ бути ідентичною
  • @limit може бути замінено кількістю результатів для отримання,
  • @offset - кількість результатів, які потрібно пропустити
  • Порівняйте продуктивність із попередніми рішеннями, оскільки вони можуть бути ефективнішими
  • це рішення дублює копії whereта order byпропозиції, і дасть неправильні результати, якщо вони не синхронізовані
  • з іншого боку, order byє явно, якщо це потрібно

1
-- @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

1

Спеціально для SQL-SERVER ви можете досягти цього різними способами. Для даного реального прикладу ми взяли тут таблицю клієнтів.

Приклад 1: "Встановити 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 і 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", але при цьому "ЗАМОВИТИ ЗА" обов'язково

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

Сподіваюся, це вам допоможе.


-1

На сервері SQL ви б використовували TOP разом з ROW_NUMBER ()


-1

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

Хтось може пропустити розуміння Row_Number () завжди сортувати за певним полем. У випадку, якщо нам потрібно визначити лише рядок у послідовності, слід використовувати:

ROW_NUMBER () НАД (ЗАМОВИТИ ЗА (ВИБІР НУЛЬ))

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.