Получение сообщений 1-20, 21-40, ... из базы данных - PullRequest
1 голос
/ 04 февраля 2010

Я пытаюсь создать почтовый ящик, где мы можем сгруппировать сообщения по 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-сервер, Oracle и Sybase.

Ответы [ 3 ]

4 голосов
/ 04 февраля 2010

В 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

ROW_NUMBER поддерживается также Oracle, но из-за деталей реализации немного менее эффективен, чем 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
0 голосов
/ 04 февраля 2010

Для 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
0 голосов
/ 04 февраля 2010

для 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)
...