Смещение строк в SQL Server


133

Есть ли способ в SQL Server получить результаты, начиная с заданного смещения? Например, в базе данных SQL другого типа можно:

SELECT * FROM MyTable OFFSET 50 LIMIT 25

для получения результатов 51-75. Эта конструкция не существует в SQL Server.

Как я могу сделать это, не загружая все строки, которые мне не нужны? Спасибо!


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

Ответы:


152

Я бы не стал использовать SELECT *. Укажите столбцы, которые вам действительно нужны, даже если это могут быть все столбцы.

SQL Server 2005+

SELECT col1, col2 
FROM (
    SELECT col1, col2, ROW_NUMBER() OVER (ORDER BY ID) AS RowNum
    FROM MyTable
) AS MyDerivedTable
WHERE MyDerivedTable.RowNum BETWEEN @startRow AND @endRow

SQL Server 2000

Эффективное разбиение на страницы больших наборов результатов в SQL Server 2000

Более эффективный метод разбиения на страницы больших наборов результатов


6
Почему вы предлагаете избегать SELECT, даже если вы выбираете все столбцы?
Адам Несс

12
Я уверен, что он использовал "*", потому что это было проще набрать и лучше понять смысл, чем "col1, col2, ... colN"
gillonba 05

9
Что касается того, почему бы не использовать его, это SELECT *означает, что если структура таблицы изменится, ваш запрос все равно будет выполняться, но дает другие результаты. Если столбец добавлен, это может быть полезно (хотя вам все равно нужно где-то использовать его по имени); если столбец удален или переименован, лучше, чтобы ваш SQL сломался заметно, чем код в дальнейшем ведет себя странно из-за неинициализации переменной.
IMSoP

5
выбрать все данные таблицы и вырезать? если есть 5000000000 строк? выбрать 5000000000 строк и вырезать для каждого запроса? это неэффективно для процессора и памяти сервера.
e-info128

3
Обратите внимание, что в 2012+ он реализован намного лучше. См. Ответ + Martin Smith
meridius

100

Если вы будете обрабатывать все страницы по порядку, то простое запоминание последнего значения ключа, показанного на предыдущей странице, и использование TOP (25) ... WHERE Key > @last_key ORDER BY Keyможет быть наиболее эффективным методом, если существуют подходящие индексы, позволяющие эффективно искать это, - или курсор API, если они этого не делают. ,

Для выбора произвольной страницы лучшим решением для SQL Server 2005 - 2008 R2, вероятно, является ROW_NUMBER иBETWEEN

Для SQL Server 2012+ вы можете использовать расширенное предложение ORDER BY для этой необходимости.

SELECT  *
FROM     MyTable 
ORDER BY OrderingColumn ASC 
OFFSET  50 ROWS 
FETCH NEXT 25 ROWS ONLY 

Хотя еще неизвестно, насколько хорошо будет работать этот вариант .


2
Теперь он доступен в SQL Server Compact 4.0 -> msdn.microsoft.com/en-us/library/gg699618(v=sql.110).aspx
Барт Веркойейен,

13
Пора добавить это в tSQL
JohnFx

3
Только для Sql Server 2012 :(
e-info128

22

Это один из способов (SQL2000)

SELECT * FROM
(
    SELECT TOP (@pageSize) * FROM
    (
        SELECT TOP (@pageNumber * @pageSize) *
        FROM tableName 
        ORDER BY columnName ASC
    ) AS t1 
    ORDER BY columnName DESC
) AS t2 
ORDER BY columnName ASC

и это другой способ (SQL 2005)

;WITH results AS (
    SELECT 
        rowNo = ROW_NUMBER() OVER( ORDER BY columnName ASC )
        , *
    FROM tableName 
) 
SELECT * 
FROM results
WHERE rowNo between (@pageNumber-1)*@pageSize+1 and @pageNumber*@pageSize

Чтобы прояснить первый ... (@pageSize) - это фактическое значение. Вам нужно будет сделать ТОП 25 специально; SQL Server 2000 не поддерживает переменные в предложении TOP. Это затрудняет динамический SQL.
Коуэн,

5
Это решение для SQL2000 не работает для последней страницы в наборе результатов, если только общее число строк не кратно размеру страницы.
Билл Карвин,

10

Вы можете использовать ROW_NUMBER()функцию, чтобы получить то, что хотите:

SELECT *
FROM (SELECT ROW_NUMBER() OVER(ORDER BY id) RowNr, id FROM tbl) t
WHERE RowNr BETWEEN 10 AND 20

7

Есть OFFSET .. FETCHв SQL Server 2012, но вам нужно будет указатьORDER BY столбец.

Если у вас действительно нет явного столбца, который вы могли бы передать как ORDER BYстолбец (как предлагали другие), вы можете использовать этот трюк:

SELECT * FROM MyTable 
ORDER BY @@VERSION 
OFFSET 50 ROWS FETCH NEXT 25 ROWS ONLY

... или

SELECT * FROM MyTable 
ORDER BY (SELECT 0)
OFFSET 50 ROWS FETCH NEXT 25 ROWS ONLY

Мы используем его в jOOQ, когда пользователи явно не указывают порядок. Это приведет к довольно случайному порядку без каких-либо дополнительных затрат.


6

Для таблиц с большим количеством столбцов данных я предпочитаю:

SELECT 
  tablename.col1,
  tablename.col2,
  tablename.col3,
  ...
FROM
(
  (
    SELECT
      col1
    FROM 
    (
      SELECT col1, ROW_NUMBER() OVER (ORDER BY col1 ASC) AS RowNum
      FROM tablename
      WHERE ([CONDITION])
    )
    AS T1 WHERE T1.RowNum BETWEEN [OFFSET] AND [OFFSET + LIMIT]
  )
  AS T2 INNER JOIN tablename ON T2.col1=tablename.col1
);

-

[CONDITION] can contain any WHERE clause for searching.
[OFFSET] specifies the start,
[LIMIT] the maximum results.

Он имеет гораздо лучшую производительность для таблиц с большими данными, такими как BLOB, потому что функция ROW_NUMBER должна просматривать только один столбец, и только соответствующие строки возвращаются со всеми столбцами.


5

Смотрите мой выбор для paginator

SELECT TOP @limit * FROM (
   SELECT ROW_NUMBER() OVER (ORDER BY colunx ASC) offset, * FROM (

     -- YOU SELECT HERE
     SELECT * FROM mytable


   ) myquery
) paginator
WHERE offset > @offset

Это решает нумерацию страниц;)


3
SELECT TOP 75 * FROM MyTable
EXCEPT 
SELECT TOP 50 * FROM MyTable

Производительность не кажется оптимальной, так как запрос без необходимости выполняется дважды. Тем более, что пользователь переходит на более высокие страницы вверх, запрос на удаление строк, то есть часть ниже ИСКЛЮЧИТЬ, будет занимать все больше и больше времени.
vanval

2

В зависимости от вашей версии вы не можете сделать это напрямую, но вы можете сделать что-нибудь взломанное, например

select top 25 *
from ( 
  select top 75 *
  from   table 
  order by field asc
) a 
order by field desc 

где «поле» - это ключ.


4
Это решение для SQL2000 не работает для последней страницы в наборе результатов, если только общее число строк не кратно размеру страницы.
Билл Карвин,

2

Ниже будут отображены 25 записей, за исключением первых 50 записей, работающих в SQL Server 2012.

SELECT * FROM MyTable ORDER BY ID OFFSET 50 ROWS FETCH NEXT 25 ROWS ONLY;

вы можете заменить идентификатор по своему усмотрению


Пожалуйста, также добавьте, что это возможно в SQL SERVER 2012
Усман Юнас

2

Вы должны быть осторожны при использовании ROW_NUMBER() OVER (ORDER BY)оператора, поскольку производительность довольно низкая. То же самое касается использования общих табличных выражений ROW_NUMBER(), но это еще хуже. Я использую следующий фрагмент, который оказался немного быстрее, чем использование табличной переменной с идентификатором для предоставления номера страницы.

DECLARE @Offset INT = 120000
DECLARE @Limit INT = 10

DECLARE @ROWCOUNT INT = @Offset+@Limit
SET ROWCOUNT @ROWCOUNT

SELECT * FROM MyTable INTO #ResultSet
WHERE MyTable.Type = 1

SELECT * FROM
(
    SELECT *, ROW_NUMBER() OVER(ORDER BY SortConst ASC) As RowNumber FROM
    (
        SELECT *, 1 As SortConst FROM #ResultSet
    ) AS ResultSet
) AS Page
WHERE RowNumber BETWEEN @Offset AND @ROWCOUNT

DROP TABLE #ResultSet

Это вернет 11 строк, а не 10.
Аарон Бертран

1

Я использую эту технику для нумерации страниц. Я беру не все строки. Например, если на моей странице должны отображаться 100 верхних строк, я беру только 100 с предложением where. Выходные данные SQL должны иметь уникальный ключ.

В таблице есть следующее:

ID, KeyId, Rank

Один и тот же ранг будет присвоен более чем одному ключевому идентификатору.

SQL это select top 2 * from Table1 where Rank >= @Rank and ID > @Id

Впервые я выставил 0 для обоих. Во второй раз проходят 1 и 14. В третий раз проходят 2 и 6 ....

Значение десятой записи Rank & Id передается следующему

11  21  1
14  22  1
7   11  1
6   19  2
12  31  2
13  18  2

Это будет наименьшей нагрузкой на систему.


1

В SqlServer2005 вы можете сделать следующее:

DECLARE @Limit INT
DECLARE @Offset INT
SET @Offset = 120000
SET @Limit = 10

SELECT 
    * 
FROM
(
   SELECT 
       row_number() 
   OVER 
      (ORDER BY column) AS rownum, column2, column3, .... columnX
   FROM   
     table
) AS A
WHERE 
 A.rownum BETWEEN (@Offset) AND (@Offset + @Limit-1) 

Не должно ли это быть @Offset + @Limit - 1? Если @Limit равен 10, это вернет 11 строк.
Аарон Бертран

1

Лучший способ сделать это, не тратя время на заказ записей:

select 0 as tmp,Column1 from Table1 Order by tmp OFFSET 5000000 ROWS FETCH NEXT 50 ROWS ONLY

это занимает меньше секунды!
лучшее решение для больших столов.


0

Я уже некоторое время искал этот ответ (для общих запросов) и нашел другой способ сделать это на SQL Server 2000+, используя ROWCOUNT и курсоры, без TOP или какой-либо временной таблицы.

Используя SET ROWCOUNT [OFFSET+LIMIT] вы можете ограничить результаты, а с помощью курсоров перейти непосредственно к нужной строке, а затем выполнить цикл до конца.

Итак, ваш запрос будет таким:

SET ROWCOUNT 75 -- (50 + 25)
DECLARE MyCursor SCROLL CURSOR FOR SELECT * FROM pessoas
OPEN MyCursor
FETCH ABSOLUTE 50 FROM MyCursor -- OFFSET
WHILE @@FETCH_STATUS = 0 BEGIN
    FETCH next FROM MyCursor
END
CLOSE MyCursor
DEALLOCATE MyCursor
SET ROWCOUNT 0

Мне бы очень не хотелось видеть это, когда вы подойдете к концу таблицы ...
Аарон Бертран,

0

В SQL Server 2012 (11.x) и более поздних версиях и в базе данных SQL Azure у вас также может быть "fetch_row_count_expression", вы также можете иметь предложение ORDER BY вместе с этим.

USE AdventureWorks2012;  
GO  
-- Specifying variables for OFFSET and FETCH values    
DECLARE @skip int = 0  , @take int = 8;  
SELECT DepartmentID, Name, GroupName  
FROM HumanResources.Department  
ORDER BY DepartmentID ASC   
    OFFSET @skip ROWS   
    FETCH NEXT @take ROWS ONLY; 

https://docs.microsoft.com/en-us/sql/t-sql/queries/select-order-by-clause-transact-sql?view=sql-server-ver15

Примечание. СМЕЩЕНИЕ. Указывает количество строк, которые нужно пропустить, прежде чем он начнет возвращать строки из выражения запроса. Это НЕ номер начального ряда. Итак, чтобы включить первую запись, должно быть 0.

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