ОБМЕЖЕННЯ 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. Dapper розбивається на це на карті, тому я не хочу перейменовувати їх, але я не можу використовувати псевдоніми 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для отримання перших рядків підрахунку + зміщення , а потім використання API для проходження перших рядків зміщення .

Дивитися також:


135

Для SQL Server 2012 + ви можете використовувати .

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

10
SQl Server 2012 вимагає вказати ЗАМОВИТИ ПО, коли ви використовуєте OFFSET 5 ROWS FETCH NEXT 5 ROWS ONLY, а MySql і SQLite не вимагає ORDER BY, коли ви використовуєте LIMIT 5,5
Tomas Kubes

4
@ qub1n - Однак, MySQL не гарантує, які рядки ви отримаєте в цьому випадку.
Мартін Сміт

3
Чи потрібно використовувати offset, чи ви можете залишити цю лінію поза межами (якщо припустити, що ви не хочете компенсувати)?
Cullub


Ви, наприклад, запит працює нормально, але якщо я змінити ім'я таблиці та порядок на колонки, як нижче, ВИБІРТИ * З DimProduct ЗАМОВЛЕННЯ ProductKey OFFSET 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 «s відповіді ставиться на голосування, після порівняння плану виконання з таким підходом х, я виявив, що це рішення працює так швидше.
Суворий

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 () не може нумерувати рядки так, як вони виходять із ЗАМОВЛЕННЯ? Мені завжди було цікаво, чому "НАДІЙ (ЗАМОВИТИ ІМЯ)" є обов'язковим, але я думаю, що для цього є вагомі причини. Або , по крайней мере , причина.
Томалак

3
бо не існує такого поняття, як наказ без наказу за пунктом. Ви отримуєте будь-який порядок, щоб записи були доступні для сервера, і це може змінитися від запиту на запит до запиту.
Joel Coehoorn

1
@marcgg: Я ніколи не читав жодних ознак того, що Microsoft планує впровадити LIMIT. Навіть якщо у них такий план, постачальники із закритим кодом, як правило, не попередньо оголошують функції. Це, безумовно, буде корисною функцією, але ми не знаємо, яку кількість роботи було б реалізувати, враховуючи їх код.
Білл Карвін

3
Якщо ви не хочете повторити себе в пункті ЗАМОВЛЕННЯ ПО, використовуйте псевдонім ROW_NUMBER (), а не оригінальний набір стовпців.
Петро Радохія

2
@Tomalak: Що стосується SQL Server, впорядкування, яке використовується для обчислення ROW_NUMBER (), абсолютно не пов'язане з впорядкуванням набору результатів. Ось чому ви повинні вказати їх окремо.
ЛукаХ

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

Примітка Pelase: остання сторінка все ще міститиме рядки 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 вимагає ОКОН, але ви можете замовити будь-що:

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 ()) (упорядкувати (виберіть 1)) як RowIndex, * з таблиці), виберіть топ 10 * від tbl, де RowIndex> = 10


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

він буде друкувати від 15 до 25, як ліміт у MYSQl


2
Що робити, якщо користувач видалив запис між 15 і 25?
Gökçer Gökdal
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.