Эффективный пейджинговый (лимитный) запрос в SQLServer 2000? - PullRequest
9 голосов
/ 02 февраля 2009

Какой самый эффективный способ сделать пейджинговый запрос в SQLServer 2000?

Где «запрос подкачки» будет эквивалентен использованию оператора LIMIT в MySQL.

РЕДАКТИРОВАТЬ: может ли хранимая процедура быть более эффективной, чем любой запрос на основе набора в этом случае?

Ответы [ 5 ]

11 голосов
/ 06 февраля 2009

Пейджинг больших наборов результатов , и победитель использует RowCount. Также есть обобщенная версия для более сложных запросов. Но отдайте должное Жасмин Мухаремович :)

DECLARE @Sort /* the type of the sorting column */
SET ROWCOUNT @StartRow
SELECT @Sort = SortColumn FROM Table ORDER BY SortColumn
SET ROWCOUNT @PageSize
SELECT ... FROM Table WHERE SortColumn >= @Sort ORDER BY SortColumn

Статья содержит весь исходный код.

Пожалуйста, ознакомьтесь с информацией "Обновление 2004-05-05". !

6 голосов
/ 02 февраля 2009

Я думаю, что вложенный запрос SELECT TOP n, вероятно, самый эффективный способ выполнить его.

SELECT TOP ThisPageRecordCount *
FROM Table
WHERE ID NOT IN (SELECT TOP BeforeThisPageRecordCount ID FROM Table ORDER BY OrderingColumn)
ORDER BY OrderingColumn

Заменить ThisPageRecordCount на элементы на странице и BeforeThisPageRecordCount на (PageNumber - 1) * items-per-page.

Конечно, лучший способ в SQL Server 2005 - использовать функцию ROW_NUMBER() в CTE.

1 голос
/ 07 февраля 2009

Эффективность запроса действительно зависит от того, как структурирована базовая таблица. Если, скажем, у вас есть первичный ключ с именем ID, который является IDENTITY, и это кластеризованный индекс, и , вы можете предположить, что никто не делал IDENTITY_INSERT для него, вы можете сделать запрос как:

ВЫБРАТЬ ТОП XXX ИЗ таблицы WHERE ID> @LastPagesID;

Это даст вам результаты как можно быстрее. Все остальное, что будет действительно эффективным, - это какой-то вариант - может быть, это не идентификатор - может быть, то, что вы используете на странице, на самом деле является датой, которую вы знаете, чтобы быть уникальной, но вы понимаете ... Показанные здесь запросы на основе IN (), вероятно, будут работать, но они не будут влиять на производительность частичного кластерного сканирования или сканирования индекса.

0 голосов
/ 09 февраля 2009

Это общая хранимая процедура SQL Server 2000, которая выполняет разбиение на страницы для любой таблицы. Хранимая процедура принимает имя таблицы, столбцы для вывода (по умолчанию все столбцы в таблице), необязательное условие WHERE, необязательный порядок сортировки, номер страницы для извлечения и количество строк на странице.

    CREATE PROCEDURE [dbo].[GetPage]
    @pTableName VARCHAR(30),
    @pColumns VARCHAR(200) = '*',
    @pFilter VARCHAR(200) = '',
    @pSort VARCHAR(200) = '',
    @pPage INT = 1,
    @pPageRows INT = 10
    AS

    SET NOCOUNT ON
    DECLARE @vSQL VARCHAR(4000)
    DECLARE @vTempTable VARCHAR(30)
    DECLARE @vRowStart INT
    DECLARE @vTotalRows INT

    SET @vTempTable = '##Tmp' + CAST(DATEPART(YYYY, GETDATE()) AS VARCHAR(4)) +
    CAST(DATEPART(MM, GETDATE()) AS VARCHAR(2)) +
    CAST(DATEPART(DD, GETDATE()) AS VARCHAR(2)) +
    CAST(DATEPART(HH, GETDATE()) AS VARCHAR(2)) +
    CAST(DATEPART(MI, GETDATE()) AS VARCHAR(2)) +
    CAST(DATEPART(SS, GETDATE()) AS VARCHAR(2)) +
    CAST(DATEPART(MS, GETDATE()) AS VARCHAR(3))

    SET @vSQL = 'SELECT ' + @pColumns + ', IDENTITY(INT, 1, 1) AS ROWID INTO ' + @vTempTable + ' FROM ' + @pTableName

    IF @pFilter != '' AND @pFilter IS NOT NULL
    SET @vSQL = @vSQL + ' WHERE ' + @pFilter

    IF @pSort != '' AND @pSort IS NOT NULL
    SET @vSQL = @vSQL + ' ORDER BY ' + @pSort

    EXECUTE (@vSQL)

    -- Get the total number of rows selected
    SET @vTotalRows = @@ROWCOUNT

    -- If page number = 0, set it to the first page
    IF @pPage = 0
    SET @pPage = 1

    -- If page number is beyond the last page, set page to the last page
    IF (@pPage * @pPageRows) > @vTotalRows
    BEGIN
    SET @pPage = @vTotalRows / @pPageRows
    IF (@vTotalRows % @pPageRows) != 0
    SET @pPage = @pPage + 1
    END

    SET @vRowStart = ((@pPage - 1) * @pPageRows) + 1
    SET @vSQL = 'SELECT * FROM ' + @vTempTable + ' WHERE ROWID BETWEEN ' + CAST(@vRowStart AS VARCHAR(10)) +
    ' AND ' + CAST((@vRowStart + @pPageRows - 1) AS VARCHAR(10)) + ' ORDER BY ROWID'
    EXECUTE (@vSQL)

    SET @vSQL = 'DROP TABLE ' + @vTempTable
    EXECUTE (@vSQL)

GO

Вот несколько примеров того, как использовать его с помощью базы данных Northwing:

EXECUTE [dbo].[GetPage] 'Customers', '*', '', '', 1, 10
EXECUTE [dbo].[GetPage] 'Customers', '*', '', 'CustomerID DESC', 1, 10

Для подтверждения, это не моя работа, а любезность http://www.eggheadcafe.com/PrintSearchContent.asp?LINKID=1055

Ура, Джон

0 голосов
/ 09 февраля 2009

Я думаю, что у вас действительно есть веская причина для перехода на SQL 2005.

В SQL 2005 это можно сделать быстро и легко с помощью:

select ROW_NUMBER() over (order by [MyField]) as rowNum, *
from [MyTable]
where rowNum between @firstRow and @lastRow

Если вы действительно застряли с SQL 2000, я бы волновался - Microsoft не собирается полностью поддерживать его гораздо дольше, если учесть, что теперь это два поколения назад.

Боюсь, что лучшего способа сделать это не будет - все решения вроде хаки.

@ Ответ Петра Петрова, вероятно, самый последовательный:

  • Если вы имеете дело с кластеризованным индексом для таблицы меньшего размера для вашей сортировки, то метод ASC-DESC (динамическое построение двух сортировок в одну сторону с использованием TOP), вероятно, будет быстрее.
  • Если ваши данные относительно статичны, а сортировка фиксирована, вы можете добавить свое собственное поле rowNum, которое вы обновляете при изменении порядка сортировки (звучит ужасно, но быстро для больших таблиц).

Я думаю, вы каждый раз просматриваете несколько часов работы с анализатором запросов. Хранимый процесс не будет иметь большого значения, так как кэширование плана запроса вряд ли станет узким местом.

...