2013-05-31 10 views
10

Sto cercando di implementare una routine di paging un po 'diversa.T-SQL: Paging WITH TIES

Ai fini di un semplice esempio, supponiamo che ho un tavolo definita e popolata come segue:

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 sarà sempre lo stesso intervallo, ad esempio dati giornalieri, dati 1 ora, dati 1 minuto, ecc. Non saranno mischiati.

Ai fini di reporting e di presentazione, voglio implementare il paging che:

  1. Ordini tramite TimeStamp
  2. inizia con un suggerito pageSize (diciamo 4), ma regola automaticamente per includere ulteriori record corrispondenti a TimeStamp. In altre parole, se 1/1/2013 01:00 è incluso per uno ParentId, lo pageSize suggerito verrà sovrascritto e tutti i record per l'ora 01:00 saranno inclusi per tutti ParentId's. È quasi come l'opzione TOP WITH TIES.

Quindi eseguire questa query con pageSize di 4 restituirebbe 6 record. Ci sono 3 ore 00:00 e 1 ora 01:00 per impostazione predefinita, ma poiché ci sono più ore 01:00's, il pageSize verrà sostituito per restituire tutte le ore 00:00 e 01:00.

Ecco quello che ho finora, e penso di essere vicino come funziona per la prima iterazione, ma le query sequenziali per le prossime righe pageSize+ non funzionano.

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 

Il ROW_NUMBER garantisce il rispetto del pageSize minimo, ma il RANK includerà ulteriori vincoli.

+0

quale versione di SQL Server? – gbn

+1

Siamo spiacenti. Avrei dovuto dire che stavo usando 2008. –

+1

dovresti usare una condizione su timestamp. Per la prima query 'timestamp> minValue' (a seconda del tipo di colonna datetime o datetime2). Dal risultato della query si legge un valore massimo per il timestamp. Per la seguente query si utilizza questo filtro massimo per filtrare: 'selezionare il primo 4 con i legami * da @temp dove timestamp> max' – tschmit007

risposta

0

Penso che la vostra strategia di utilizzo di row_number() e rank() sia un complicazione eccessiva.

Basta selezionare i primi 4 timestamp dai dati. Quindi scegliere qualsiasi timestamp che corrisponde a quelli:

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

Questo è praticamente quello che ho finito per fare. L'uso di row_number e rank lo rendeva molto più difficile. Grazie per l'input. –

1
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; 

EDIT: Una query alternativa che assegna numeri di pagina come va, cosicché pagina successiva/precedente può essere attuato senza file sovrapposte:

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) 

noti che ricorsiva CTE spesso scala mal .

+0

Grazie per la risposta, ma PageSize = 1 e 2, entrambi restituiscono i dati dell'ora 01:00. –

+0

@JohnRussell - Funziona per me nella SS 2008 R2. Con una dimensione di pagina pari a 1, le pagine 1, 2 e 3 restituiscono lo stesso set di dati di mezzanotte. Le pagine 4, 5 e 6 restituiscono l'insieme dei dati 1am. Puoi cambiare il 'select' finale in' select * from Alpha' o 'select * from Beta' per vedere cosa sta succedendo. – HABO

+0

Penso che il problema con questa soluzione sia che le pagine si sovrappongono. Pagina n inizia a registrare dopo (n-1) * pagine a prescindere dal fatto che le pagine precedenti fossero più grandi del formato pagine specificato. – Chad