10

我有一个包含超过200 万条记录的数据库,我需要执行分页以显示在我的 Web 应用程序上,该应用程序必须在DataGrid.

我已经尝试过使用ROW_NUMBER(),但是这种方式会选择所有 200 万条记录,然后只得到 10 条记录。我也尝试使用TOP 10,但我必须保存第一个和最后一个 id 来控制页面。而且我读过 usingDataAdapter.Fill()将选择所有内容,然后获取我需要的 10 条记录。

哪个是最好的方法?我应该使用DataAdapter.Fill()吗?还是使用 SQL Server 的功能ROW_NUMBER()?或者尝试使用TOP 10

4

3 回答 3

5
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 (@FromRow+@PgSize-1)
END

那是我用于分页的查询。使用它,您将在 4-5 秒内获得您想要的 10 条记录。我在 3 秒内获得 10 条记录,我的数据库中的总记录为 1000 万条,不要使用前 10 名,它每次只会带来相同的 10 条记录。在我的情况下,我在会话中维护页面大小和起始行号(@FromRow),并将这两个值传递给下面给定的存储过程并获得结果。此外,如果您使用的是 SQL 2012,您可能希望使用 OFFSET 和 Fetch next 10 rows 之类的东西。在 google 上搜索 OFFSET 关键字,您将在顶部看到您想要的结果。

谢谢

于 2013-01-17T10:45:33.890 回答
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();
}

希望能帮助到你。

于 2013-01-17T14:35:31.647 回答
1

我使用以下模式(自动)生成分页子查询:

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条款应该是相同的;sql server 的优化器通常足够聪明,可以理解这一点。top重复是用于截断结果的子句的副作用;top对未排序的子查询是不合法的。top 有助于帮助查询优化器了解此查询可能返回几行。
  • 使用基于页码 + 大小的参数@takeN@skipN原因相对较小。首先,它在查询中更灵活一点,更简单一点,其次,它更好地发挥了 sql server 的优势:DB 在优化这类查询方面并不是特别出色,希望是像这样的外部简单顶部子句使优化器了解可能的最大行数变得微不足道。一般来说,我尽量避免在 sql 中进行计算,因为它往往会混淆优化器(尽管在特定情况下 @pagecount*@pagesize 实验表明这不是一个大问题)

请注意,sql server 2012 支持一个新的offset...fetch 子句,正是这种情况要简单得多。

于 2013-01-17T12:11:10.167 回答