zoukankan      html  css  js  c++  java
  • SQL Server 中的三种分页方式

    USE tempdb
    GO
    SET NOCOUNT ON
    
    --创建表结构
    IF OBJECT_ID(N'ClassB', N'U') IS NOT NULL
        DROP TABLE ClassB
    GO
    CREATE TABLE ClassB(ID INT PRIMARY KEY, Name VARCHAR(16), CreateDate DATETIME, AID INT, Status INT)
    CREATE INDEX IDX_CreateDate ON ClassB(CreateDate)
    CREATE INDEX IDX_AID ON ClassB(AID)
    GO
    
    --插入测试数据
    DECLARE @ID INT
    SET @ID = 1
    WHILE @ID <= 100000
    BEGIN
        INSERT INTO ClassB VALUES(@ID, 'fx', GETDATE(), @ID % 20, @ID % 20)
        SET @ID = @ID + 1
    END
    
    --统计总行数
    SELECT 'ClassB' AS ClassB, count(1) AS Count FROM ClassB

    查询条件如下:根据CreateDate倒序排序,CreateDate一致时按照ID倒序;时间区间在2014-07-13和2014-07-14之间;每页20条,当前页数第3页;

    方案A:双Top,取出之前页的所有ID后,使用NOT IN排除这些ID进行查询,这是性能最差一种,因为他使用相同的查询谓词,查询了两次数据,且两次排序;

    DECLARE @page_size INT = 20;
    DECLARE @page_index INT = 3;
    --A: 双Top:取出之前页的所有ID后,使用NOT IN排除这些ID进行查询,性能最差
    WITH ID_List_Excluded AS
    (
        SELECT TOP (@page_size * (@page_index - 1)) ID FROM ClassB WHERE CreateDate BETWEEN '2014-07-13' AND '2014-07-14' 
        ORDER BY CreateDate DESC, ID DESC
    )
    SELECT TOP (@page_size) * FROM ClassB WHERE ID NOT IN (SELECT ID FROM ID_List_Excluded) AND CreateDate BETWEEN '2014-07-13' AND '2014-07-14' 
    ORDER BY CreateDate DESC, ID DESC;

    表 'ClassB'。扫描计数 2,逻辑读取 136 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。
    表 'Worktable'。扫描计数 1,逻辑读取 197 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。

    查询页数越靠后逻辑读取,下面是查询第30页的逻辑读取情况:

    表 'ClassB'。扫描计数 2,逻辑读取 1243 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。
    表 'Worktable'。扫描计数 1,逻辑读取 2574 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。

    方案B:ROW_NUMBER,取出截止到当前页所有符合查询谓词的记录,并给每条记录加上序号,然后根据序号查询,性能次之;

    DECLARE @page_size INT = 20;
    DECLARE @page_index INT = 3;
    --B: ROW_NUMBER:取出截至到当前页所有符合查询谓词的记录,并给每条记录加上序号,然后根据序号查询,性能次之
    WITH NewClassB AS
    (
        SELECT TOP (@page_size * @page_index) ROW_NUMBER() OVER (ORDER BY CreateDate DESC, ID DESC) AS ROWID, * FROM ClassB 
        WHERE CreateDate BETWEEN '2014-07-13' AND '2014-07-14'
    )
    SELECT TOP (@page_size) * FROM NewClassB 
    WHERE ROWID BETWEEN (@page_size * (@page_index - 1) + 1) AND (@page_size * @page_index);

    表 'ClassB'。扫描计数 1,逻辑读取 134 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。

    方案B已经比方案A快了近一倍,少了一个Worktable,同样,page_index越大,扫描次数越多,因为子查询中要查询多余的(之前页)数据和字段,下面是查询第30页时的IO读取情况;

    表 'ClassB'。扫描计数 1,逻辑读取 1241 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。

    方案C:双Top,取出截止到当前页为止的所有的ID,去掉之前页的ID,然后根据ID(通常是主键)作常规查询,性能最优;

    DECLARE @page_size INT = 20;
    DECLARE @page_index INT = 3;
    WITH ID_List AS
    (
        SELECT TOP (@page_size * @page_index) ID FROM ClassB 
        WHERE CreateDate BETWEEN '2014-07-13' AND '2014-07-14' 
        ORDER BY CreateDate DESC, ID DESC
    )
    ,ID_List_Current AS
    (
        SELECT ID FROM ID_List WHERE ID NOT IN(SELECT TOP (@page_size * (@page_index - 1)) ID FROM ID_List)
    )
    SELECT * FROM ClassB WHERE ID IN (SELECT ID FROM ID_List_Current);

    表 'ClassB'。扫描计数 2,逻辑读取 52 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。

    查询第30页的数据仅比查询第3页的数据多出两次逻辑读取,方案C和B的唯一在于:子查询中的查询列,方案B中返回了所有字段,所有要通过聚集索引走lookup,而方案C直接返回索引键即可。

    表 'ClassB'。扫描计数 2,逻辑读取 54 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。

  • 相关阅读:
    warning: ISO C++ forbids converting a string constant to 'char*' [-Wwrite-strings]
    Windows10+CLion+OpenCV4.5.2开发环境搭建
    Android解决部分机型WebView播放视频全屏按钮灰色无法点击、点击全屏白屏无法播放等问题
    MediaCodec.configure Picture Width(1080) or Height(2163) invalid, should N*2
    tesseract
    Caer -- a friendly API wrapper for OpenCV
    Integrating OpenCV python tool into one SKlearn MNIST example for supporting prediction
    Integrating Hub with one sklearn mnist example
    What is WSGI (Web Server Gateway Interface)?
    Hub --- 机器学习燃料(数据)的仓库
  • 原文地址:https://www.cnblogs.com/fengxiang/p/3841287.html
Copyright © 2011-2022 走看看