Я искал разные способы написания хранимой процедуры для возврата «страницы» данных.Это было для использования с ASP ObjectDataSource
, но это может считаться более общей проблемой.
Требуется вернуть подмножество данных на основе обычных параметров пейджинга;startPageIndex
и maximumRows
, но также параметр sortBy
, позволяющий сортировать данные.Также есть несколько параметров, переданных для фильтрации данных по различным условиям.
Один из распространенных способов сделать это выглядит примерно так:
[Метод 1]
;WITH stuff AS (
SELECT
CASE
WHEN @SortBy = 'Name' THEN ROW_NUMBER() OVER (ORDER BY Name)
WHEN @SortBy = 'Name DESC' THEN ROW_NUMBER() OVER (ORDER BY Name DESC)
WHEN @SortBy = ...
ELSE ROW_NUMBER() OVER (ORDER BY whatever)
END AS Row,
.,
.,
.,
FROM Table1
INNER JOIN Table2 ...
LEFT JOIN Table3 ...
WHERE ... (lots of things to check)
)
SELECT *
FROM stuff
WHERE (Row > @startRowIndex)
AND (Row <= @startRowIndex + @maximumRows OR @maximumRows <= 0)
ORDER BY Row
Одна проблема с этим заключается в том, что он не дает общего подсчета, и обычно для этого нам нужна другая хранимая процедура.Эта вторая хранимая процедура должна реплицировать список параметров и сложное предложение WHERE
.Не хорошо.
Одним из решений является добавление дополнительного столбца в окончательный список выбора (ВЫБЕРИТЕ СЧЕТЧИК (*) ИЗ материала) AS TotalRows
.Это дает нам сумму, но повторяет ее для каждой строки в наборе результатов, что не идеально.
[Метод 2]
Здесь приводится интересная альтернатива (http://www.4guysfromrolla.com/articles/032206-1.aspx) с использованием динамического SQLОн считает, что производительность лучше, потому что оператор CASE в первом решении затягивает. Достаточно справедливо, и это решение позволяет легко получить totalRows и добавить его в выходной параметр. Но я ненавижу кодировать динамический SQL. Все это'bit of SQL' + STR (@ parm1) + 'bit more SQL' gubbins.
[Метод 3]
Единственный способ найти то, что я хочу, без повторения кода, который будет иметьБыть синхронизированным и поддерживать разумную читабельность - значит вернуться к «старому способу» использования табличной переменной:
DECLARE @stuff TABLE (Row INT, ...)
INSERT INTO @stuff
SELECT
CASE
WHEN @SortBy = 'Name' THEN ROW_NUMBER() OVER (ORDER BY Name)
WHEN @SortBy = 'Name DESC' THEN ROW_NUMBER() OVER (ORDER BY Name DESC)
WHEN @SortBy = ...
ELSE ROW_NUMBER() OVER (ORDER BY whatever)
END AS Row,
.,
.,
.,
FROM Table1
INNER JOIN Table2 ...
LEFT JOIN Table3 ...
WHERE ... (lots of things to check)
SELECT *
FROM stuff
WHERE (Row > @startRowIndex)
AND (Row <= @startRowIndex + @maximumRows OR @maximumRows <= 0)
ORDER BY Row
(или аналогичный метод с использованием столбца IDENTITY в табличной переменной).Я могу просто добавить SELECT COUNT в табличную переменную, чтобы получить totalRows и поместить его в выходной параметр.
Я провел несколько тестов и с довольно простой версией oВ запросе (без sortBy и без фильтра) метод 1, кажется, идет впереди (почти в два раза быстрее, чем другие 2).Затем я решил проверить, вероятно, мне нужна была сложность, и мне нужно было, чтобы SQL был в хранимых процедурах.При этом я получаю метод 1, который занимает почти вдвое больше времени, чем другие 2 метода.Что кажется странным.
Есть ли веская причина, почему я не должен отвергать CTE и придерживаться метода 3?
ОБНОВЛЕНИЕ - 15 марта 2012
Я пыталсяадаптируя метод 1 для выгрузки страницы из CTE во временную таблицу, чтобы я мог извлечь TotalRows, а затем выбрать только соответствующие столбцы для набора результатов.Это, казалось, значительно прибавило времени (больше, чем я ожидал).Я должен добавить, что я запускаю это на ноутбуке с SQL Server Express 2008 (все, что у меня есть), но сравнение все равно должно быть верным.
Я снова посмотрел на метод динамического SQL.Оказывается, я на самом деле не делал это правильно (просто объединял строки).Я установил его как в документации для sp_executesql
(со строкой описания параметра и списком параметров), и он стал намного более читаемым.Также этот метод работает быстрее всего в моей среде.Почему это должно все еще сбивать с толку меня, но я думаю, что ответ намекает на комментарий Хогана.