Каков наилучший способ выполнения разбиения на страницы на SQL Server?
У меня есть база данных с более чем 2 миллионами записей, и мне нужно выполнить разбиение на страницы, чтобы показать в моем веб-приложении, которое должно содержать 10 записей на странице в DataGrid
.
Я уже пытался использовать ROW_NUMBER()
, но этот путь выберет все 2 миллиона записей, а затем получит всего 10 записей. Я также попытался использовать TOP 10
, но мне пришлось бы сохранить первый и последний идентификаторы для управления страницами. И я читал, что с помощью DataAdapter.Fill()
выберет все содержимое и затем получит 10 записей, которые мне нужны.
Каков наилучший способ? Должен ли я использовать DataAdapter.Fill()
? Или используйте функцию SQL Server ROW_NUMBER()
? Или попробуйте использовать TOP 10
?
Ответы
Ответ 1
ALTER PROCEDURE [dbo].[SP_tblTest_SelectSpecificRecordsWithCTE]
@FromRow int = 1000000,
@PgSize int = 10
AS
BEGIN
;WITH RecordsRN AS
(
select ID, colValue, ROW_NUMBER() over(order by colvalue) as Num from tblTest
)
SELECT ID Value, colValue Text FROM RecordsRN WHERE Num between @FromRow AND (@[email protected])
END
это запрос, который я использую для подкачки. используйте его, и u получит ur желаемых 10 записей за 4-5 секунд. я получаю 10 записей за 3 секунды, а итоговые записи в моем db составляют 10 миллионов, не используют 10 лучших, он будет приносить только 10 записей каждый раз. в моем случае я поддерживаю размер страницы и номер строки запуска (@FromRow) в сеансе, и я передаю эти два значения в приведенную ниже хранимую процедуру и получаю результат.
Более того, если вы используете SQL 2012, вы можете использовать OFFSET и Fetch следующие 10 строк. найдите в google ключевое слово OFFSET, и вы увидите нужный результат сверху.
спасибо
Ответ 2
Используйте ROW_NUMBER()
и реализуйте статическую служебную функцию (как GetPaginatedSQL
в моем коде), которая автоматически обертывает ваш исходный запрос SQL в ограниченный/разбитый на страницы.
Это я использую:
namespace Persistence.Utils
{
public class SQLUtils
{
/// <summary>
/// Builds a paginated/limited query from a SELECT SQL.
/// </summary>
/// <param name="startRow">Start row</param>
/// <param name="numberOfRows">Number/quatity of rows to be expected</param>
/// <param name="sql">Original SQL (without its ordering clause)</param>
/// <param name="orderingClause">MANDATORY: ordering clause (including ORDER BY keywords)</param>
/// <returns>Paginated SQL ready to be executed.</returns>
/// <remarks>SELECT keyword of original SQL must be placed exactly at the beginning of the SQL.</remarks>
public static string GetPaginatedSQL(int startRow, int numberOfRows, string sql, string orderingClause)
{
// Ordering clause is mandatory!
if (String.IsNullOrEmpty(orderingClause))
throw new ArgumentNullException("orderingClause");
// numberOfRows here is checked of disable building paginated/limited query
// in case is not greater than 0. In this case we simply return the
// query with its ordering clause appended to it.
// If ordering is not spe
if (numberOfRows <= 0)
{
return String.Format("{0} {1}", sql, orderingClause);
}
// Extract the SELECT from the beginning.
String partialSQL = sql.Remove(0, "SELECT ".Length);
// Build the limited query...
return String.Format(
"SELECT * FROM ( SELECT ROW_NUMBER() OVER ({0}) AS rn, {1} ) AS SUB WHERE rn > {2} AND rn <= {3}",
orderingClause,
partialSQL,
startRow.ToString(),
(startRow + numberOfRows).ToString()
);
}
}
}
Вышеупомянутая функция может быть улучшена, но является начальной версией.
Затем, в ваших DAO, вы должны просто сделать что-то вроде этого:
using (var conn = new SqlConnection(CONNECTION_STRING))
{
using (var cmd = conn.CreateCommand())
{
String SQL = "SELECT * FROM MILLIONS_RECORDS_TABLE";
String SQLOrderBy = "ORDER BY DATE ASC "; //GetOrderByClause(Object someInputParams);
String limitedSQL = GetPaginatedSQL(0, 50, SQL, SQLOrderBy);
DataSet ds = new DataSet();
SqlDataAdapter adapter = new SqlDataAdapter();
cmd.CommandText = limitedSQL;
// Add named parameters here to the command if needed...
adapter.SelectCommand = cmd;
adapter.Fill(ds);
// Process the dataset...
}
conn.Close();
}
Надеюсь, что это поможет.
Ответ 3
Я использую следующий шаблон для (автоматически) создания подкачки подкачки:
select top (@takeN) <your-column-list>
from (
select qSub2.*, _row=row_number() over (order by SomeColumn Asc, SomethingElse Desc)
from (
select top (@takeN + @skipN) <your-column-list>
from (
select <your-subquery-here>
) as qSub1
order by SomeColumn Asc, SomethingElse Desc
) as qSub2
) qSub3
where _row > @skipN
order by _row
Примечания к этому шаблону:
- Подпрограмма концептуально пропускает строки
@skipN
, а затем берет следующие строки @takeN
.
- Если вам не нужен дополнительный столбец
_row
в результате, вы можете заменить <your-column-list>
на *
; Я использую явный список столбцов, потому что он позволяет мне подмножать набор столбцов во время выполнения, что может быть полезно, например. для поиска только столбцов ключевых слов и т.п.
- Ваши предложения
order by
должны быть одинаковыми; sqm server optmizer, как правило, достаточно умен, чтобы понять это. Дублирование является побочным эффектом предложения top
, используемого для обрезания результатов; top
не является законным для несортированных подзапросов. И верх полезен, чтобы помочь оптимизатору запросов понять, что этот запрос, скорее всего, вернет несколько строк.
- Причины использования
@takeN
и @skipN
в отличие от параметров на основе номера страницы + размера довольно незначительны. Во-первых, он немного более гибкий и немного более простой в запросе, а во-вторых, он немного повышает производительность серверов sql-серверов: БД не особенно блестяще оптимизирует эти запросы в первую очередь, и надежда что внешнее, простое верхнее предложение, как это делает тривиальным для оптимизатора, чтобы понять максимальное количество строк. В общем, я стараюсь избегать выполнения вычислений в sql. Я мог бы одинаково хорошо работать в коде, поскольку он имеет тенденцию путать оптимизатор (хотя в конкретном случае экспериментов @pagecount * @pagesize показано, что это не огромная проблема)
Обратите внимание, что SQL Server 2012 поддерживает новый смещение... предложение выборки именно для этого сценария, который намного проще.