【问题标题】:Getting messages 1-20, 21-40,... from a database从数据库中获取消息 1-20, 21-40,...
【发布时间】:2011-01-13 03:57:59
【问题描述】:

我正在尝试建立一个邮箱,我们可以在其中对 x 中的邮件进行分组。 如果您将 x 设置为 20,您将在第一页看到消息 1-20,打开第二页将显示消息 21-40,依此类推。

我如何有效地查询这个?我能想到的最好的是:

select top 20 * 
from tbl_messages
where 
tnr_id not in
(
    select top 40 tnr_id   —20/40/60/80/…
    from tbl_messages
    order by dt_made desc, tnr_id desc
)
order by dt_made desc, tnr_id desc

有没有更有效的方法来做到这一点?使用的数据库是 SQL server、oracle 和 sybase。

【问题讨论】:

    标签: sql sql-server oracle sybase


    【解决方案1】:

    Oracle:

    SELECT  *
    FROM    (
            SELECT  t.*, rownum AS rn
            FROM    tbl_messages t
            ORDER BY
                    dt_made DESC, tnr_id DESC
            )
    WHERE   rn > 40
            AND rownum <= 20
    

    SQL Server 2005 及以上:

    DECLARE @start INT
    DECLARE @pagesize INT
    SET @start = 40
    SET @pagesize = 20  
    
    SELECT  *
    FROM    (
            SELECT  TOP (@start + @pagesize)
                    t.*, ROW_NUMBER() OVER (ORDER BY dt_made DESC, tnr_id DESC) AS rn
            FROM    tbl_messages t
            ORDER BY
                    dt_made DESC, tnr_id DESC
            ) q
    WHERE   rn > @start
    

    Oracle 也支持ROW_NUMBER,但由于实现细节的原因,效率比rownum 低一点。

    在我的博客中查看这篇文章以获得性能比较:

    更新:

    如果你可以容忍并发更新带来的一些差异,你可以记住客户端当前页面上的最后一条记录,并使用它来更快地得到下一个结果:

    SELECT  TOP 20 *
    FROM    tbl_messages t
    WHERE   dt_made <= @last_dt_made
            AND NOT (dt_made = @last_dt_made AND tnr_id >= @last_tnr_id)
    ORDER BY
            dt_made DESC, tnr_id DESC
    

    【讨论】:

    • 对于sql server版本,TOP 60限制数据显示为60行,如果要翻页的数据较多,则无法显示。您将永远无法分页到第 60 行之后的数据。
    • @KM:此查询选择行 4160。要获得下一页,您需要将TOPrn 的条件相应地调整为80&gt; 60
    【解决方案2】:

    对于 SQL Server,试试这个:

    --set up a table to page through:
    DECLARE @TestTable  table (TableID int )
    
    INSERT INTO @TestTable (TableID)
    SELECT TOP 1000 row_number() over(order by t1.number) as N
    FROM master..spt_values t1 
        CROSS JOIN master..spt_values t2
    
    --set page location and size
    DECLARE @Start  int
    DECLARE @Size   int
    
    SET @Start=40
    SET @Size=20
    
    --return data for that page:
    SELECT
        *
        FROM (SELECT 
                  v.*, ROW_NUMBER() OVER (ORDER BY TableID) AS RowNumber
                  FROM  @TestTable  v
             ) dt
        WHERE RowNumber>=@Start AND RowNumber<@Start+@Size
    

    输出:

    TableID     RowNumber
    ----------- --------------------
    40          40
    41          41
    42          42
    43          43
    44          44
    45          45
    46          46
    47          47
    48          48
    49          49
    50          50
    51          51
    52          52
    53          53
    54          54
    55          55
    56          56
    57          57
    58          58
    59          59
    
    (20 row(s) affected)
    

    【讨论】:

      【解决方案3】:

      对于 SQL Server - 可能更灵活一点,让您指定页码和每页的消息

      DECLARE @pageNumber int, @messagesPerPage int
      SET @pageNumber = 3
      SET @messagesPerPage = 20
      
      SELECT *
        FROM (
              SELECT t.*, ROW_NUMBER() OVER (ORDER BY [dt_made] DESC, [tnr_id] DESC) AS __RN
                FROM tbl_messages t
             ) iDat
       WHERE __RN BETWEEN (@pageNumber - 1) * @messagesPerPage AND @pageNumber * @messagesPerPage
       ORDER BY dt_made DESC, tnr_id DESC
      

      【讨论】:

      • Msg 1033,级别 15,状态 1,第 10 行 ORDER BY 子句在视图、内联函数、派生表、子查询和公用表表达式中无效,除非还指定了 TOP 或 FOR XML。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-05-31
      • 2018-07-10
      • 2012-10-31
      • 1970-01-01
      相关资源
      最近更新 更多