Возвращает строки между определенным диапазоном, с одним выражением select

Я ищу некоторые выражения вроде этого (с использованием SQL Server 2008)

SELECT TOP 10 columName FROM tableName

Но вместо этого мне нужны значения от 10 до 20. И мне интересно, есть ли способ сделать это, используя только один оператор SELECT.

Например, это бесполезно:

SELECT columName FROM
(SELECT ROW_NUMBER() OVER(ORDER BY someId) AS RowNum, * FROM tableName) AS alias
WHERE RowNum BETWEEN 10 AND 20

Потому что выбор внутри скобок уже возвращает все результаты, и я хочу избежать этого из-за производительности.

Ответ 1

Существует трюк с row_number, который не включает сортировку всех строк.

Попробуйте следующее:

SELECT columName
FROM (SELECT ROW_NUMBER() OVER(ORDER BY (select NULL as noorder)) AS RowNum, *
      FROM tableName
     ) as alias
WHERE RowNum BETWEEN 10 AND 20

Вы не можете использовать константу в order by. Однако вы можете использовать выражение, которое оценивается константой. SQL Server распознает это и просто возвращает строки как встречающиеся, правильно перечисленные.

Ответ 2

Почему, по вашему мнению, SQL Server будет оценивать весь внутренний запрос? Предполагая, что ваш столбец сортировки проиндексирован, он будет просто читать первые 20 значений. Если вы действительно нервничаете, вы можете это сделать:

Select
  Id 
From (
  Select Top 20 -- note top 20
    Row_Number() Over(Order By Id) As RowNum,
    Id 
  From
    dbo.Test
  Order By
    Id
  ) As alias
Where
  RowNum Between 10 And 20
Order By
  Id

но я уверен, что план запросов одинаковый.

(Действительно) Исправлено в соответствии с комментарием Aaron.

http://sqlfiddle.com/#!3/db162/6

Ответ 3

Использовать SQL Server 2012 для извлечения/пропуска!

SELECT SalesOrderID, SalesOrderDetailID, ProductID, OrderQty, UnitPrice, LineTotal
FROM AdventureWorks2012.Sales.SalesOrderDetail
OFFSET 10 ROWS FETCH NEXT 10 ROWS ONLY;

Там ничего лучше, чем вы описываете для более старых версий sql-сервера. Может быть, использовать CTE, но вряд ли изменит ситуацию.

WITH NumberedMyTable AS
(
    SELECT
        Id,
        Value,
        ROW_NUMBER() OVER (ORDER BY Id) AS RowNumber
    FROM
        MyTable
)
SELECT
    Id,
    Value
FROM
    NumberedMyTable
WHERE 
    RowNumber BETWEEN @From AND @To  

или вы можете удалить первые 10 строк, а затем получить следующие 10 строк, но я бы удвоил, кто захочет это сделать.

Ответ 4

Еще один вариант

SELECT TOP(11) columName
FROM dbo.tableName
ORDER BY
CASE WHEN ROW_NUMBER() OVER (ORDER BY someId) BETWEEN 10 AND 20 
     THEN ROW_NUMBER() OVER (ORDER BY someId) ELSE NULL END DESC

Ответ 5

Вы можете создать временную таблицу, которая упорядочена так, как вы хотите:

SELECT ROW_NUMBER() OVER (ORDER BY someId) AS RowNum, * FROM tableName в ## tempTable ...

Таким образом, у вас есть упорядоченный список строк. и может просто запрашивать по номеру строки последующие времена, а не выполнять внутренний запрос несколько раз.