T-SQL: пейджинг с привязками

Я пытаюсь реализовать подкачку, которая немного отличается.

Для простого примера предположим, что у меня есть таблица, определенная и заполненная следующим образом:

DECLARE @Temp TABLE
(
    ParentId    INT,
    [TimeStamp] DATETIME,
    Value       INT
);

INSERT INTO @Temp VALUES (1, '1/1/2013 00:00', 6);
INSERT INTO @Temp VALUES (1, '1/1/2013 01:00', 7);
INSERT INTO @Temp VALUES (1, '1/1/2013 02:00', 8);
INSERT INTO @Temp VALUES (2, '1/1/2013 00:00', 6);
INSERT INTO @Temp VALUES (2, '1/1/2013 01:00', 7);
INSERT INTO @Temp VALUES (2, '1/1/2013 02:00', 8);
INSERT INTO @Temp VALUES (3, '1/1/2013 00:00', 6);
INSERT INTO @Temp VALUES (3, '1/1/2013 01:00', 7);
INSERT INTO @Temp VALUES (3, '1/1/2013 02:00', 8);

TimeStamp всегда будет тем же самым интервалом, например. ежедневные данные, данные за 1 час, данные за 1 минуту и ​​т.д. Это не будет смешанным.

Для отчетов и презентаций я хочу реализовать пейджинг, который:

  • Заказы TimeStamp
  • Запускается с использованием предлагаемого pageSize (скажем, 4), но будет автоматически настраиваться на включение дополнительных записей, соответствующих TimeStamp. Другими словами, если 1/1/2013 01:00 включен для одного ParentId, предлагаемый pageSize будет переопределен, и все записи за час 01:00 будут включены для всех ParentId's. Это почти как опция TOP WITH TIES.

Таким образом, запуск этого запроса с помощью pageSize из 4 будет возвращать 6 записей. Есть 3 часа 00:00 и 1 час 01:00 по умолчанию, но поскольку есть больше часа 01:00's, pageSize будет отменено, чтобы вернуть весь час 00:00 и 01:00.

Вот что у меня до сих пор, и я думаю, что я близок, поскольку он работает для первой итерации, но секвенционные запросы для следующих строк pageSize+ не работают.

WITH CTE AS
(
    SELECT ParentId, [TimeStamp], Value,
    RANK() OVER(ORDER BY [TimeStamp]) AS rnk,
    ROW_NUMBER() OVER(ORDER BY [TimeStamp]) AS rownum
    FROM @Temp
)

SELECT *
FROM CTE
WHERE (rownum BETWEEN 1 AND 4) OR (rnk BETWEEN 1 AND 4)
ORDER BY TimeStamp, ParentId

ROW_NUMBER обеспечивает минимальное значение pageSize, но RANK будет содержать дополнительные связи.

Ответ 1

Я думаю, что ваша стратегия использования row_number() и rank() является чрезмерно сложной.

Просто выберите верхние 4 временных метки из данных. Затем выберите любые отметки времени, которые соответствуют этим:

select *
from @temp
where [timestamp] in (select top 4 [timestamp] from @temp order by [TimeStamp])

Ответ 2

declare @Temp as Table ( ParentId Int, [TimeStamp] DateTime, [Value] Int );
insert into @Temp ( ParentId, [TimeStamp], [Value] ) values
 (1, '1/1/2013 00:00', 6),
 (1, '1/1/2013 01:00', 7),
 (1, '1/1/2013 02:00', 8),
 (2, '1/1/2013 00:00', 6),
 (2, '1/1/2013 01:00', 7),
 (2, '1/1/2013 02:00', 8),
 (3, '1/1/2013 00:00', 6),
 (3, '1/1/2013 01:00', 7),
 (3, '1/1/2013 02:00', 8);

declare @PageSize as Int = 4;
declare @Page as Int = 1;

with Alpha as (
    select ParentId, [TimeStamp], Value,
        Rank() over ( order by [TimeStamp] ) as Rnk,
        Row_Number() over ( order by [TimeStamp] ) as RowNum
    from @Temp ),
    Beta as (
    select Min( Rnk ) as MinRnk, Max( Rnk ) as MaxRnk
        from Alpha
        where ( @Page - 1 ) * @PageSize < RowNum and RowNum <= @Page * @PageSize )
    select A.*
        from Alpha as A inner join
            Beta as B on B.MinRnk <= A.Rnk and A.Rnk <= B.MaxRnk
        order by [TimeStamp], ParentId;

ИЗМЕНИТЬ: Альтернативный запрос, который присваивает номера страниц по мере их перехода, так что следующая/предыдущая страница может быть реализована без перекрывающихся строк:

with Alpha as (
    select ParentId, [TimeStamp], Value,
        Rank() over ( order by [TimeStamp] ) as Rnk,
        Row_Number() over ( order by [TimeStamp] ) as RowNum
    from @Temp ),
    Beta as (
    select ParentId, [TimeStamp], Value, Rnk, RowNum, 1 as Page, 1 as PageRow
        from Alpha
        where RowNum = 1
    union all
    select A.ParentId, A.[TimeStamp], A.Value, A.Rnk, A.RowNum,
        case when B.PageRow >= @PageSize and A.TimeStamp <> B.TimeStamp then B.Page + 1 else B.Page end,
        case when B.PageRow >= @PageSize and A.TimeStamp <> B.TimeStamp then 1 else B.PageRow + 1 end
        from Alpha as A inner join
            Beta as B on B.RowNum + 1 = A.RowNum
     )
    select * from Beta
        option ( MaxRecursion 0 )

Обратите внимание, что рекурсивные CTE часто плохо масштабируются.