Sybase offset for pagination

前端 未结 7 2095
旧巷少年郎
旧巷少年郎 2020-12-06 14:14

Is there any simple way to implement pagination in sybase? In postgres there are limit and offset in mysql there is limit X,Y. What about sybase? There is top clausure to li

7条回答
  •  遥遥无期
    2020-12-06 14:29

    I don't know if this is ASE or a different product but the following pattern works across several databases I've worked with as long as there is a way to produce a temp table with a row number somehow and you can identify a unique key for each row:

    Input parameters:

    declare @p_first int /* max number of rows to see; may be null (= all results); otherwise must be positive number */
    declare @p_skipFirst int /* number of rows to skip before the results; must be nonnegative number */
    declare @p_after PKTYPE /* key for the row before you start skipping; may be null */
    

    given a table:

    RowNumber | RowIndex | DataCol1
    1         | 1234     | Joe
    2         | 1235     | Sue
    3         | 2000     | John
    4         | 2005     | Frank
    5         | 3000     | Tom
    6         | 4000     | Alice
    

    the parameter set:

    set @p_first = 5
    set @p_skipFirst = 2
    set @p_after = 1235
    

    would represent rows 5 and 6.

    An additional set of parameters can represent paging from the end of the table in reverse:

    declare @p_last int /* max number of rows to see; may be null (= all results); otherwise must be positive number */
    declare @p_skipLast int /* number of rows to skip after the results; must be nonnegative number */
    declare @p_before PKTYPE /* key for the row after you start skipping; may be null */
    

    Assuming your unsorted table is in #resultsBeforeSort with an index column named RowIndex you can sort this with the following script:

    select RowNumber = identity(10), * 
    into #results
    from #resultsBeforeSort
    /*
    you might also wish to have a where clause on this query
    
    this sort is dynamically generated based on a sort expression and 
    ultimately ended with RowIndex to ensure a deterministic order
    */
    order by Column1, Column2 desc, RowIndex
    
    declare @p_total int, @p_min int, @p_max int
    select @p_total = count(*) from #results
    
    select @p_min = case when @p_after is null then 1 + @p_skipFirst else @p_total + 1 end
    select @p_min = RowNumber + @p_skipFirst from #results where [RowIndex] = @p_after
    
    select @p_max = case when @p_before is null then @p_total - @p_skipLast else 0 end
    select @p_max = RowNumber - @p_skipLast from #results where [RowIndex] = @p_before
    
    declare @p_min2 int, @p_max2 int
    set @p_min2 = @p_min
    set @p_max2 = @p_max
    
    select @p_max2 = case when @p_first is null then @p_max else @p_min + @p_first - 1 end
    select @p_min2 = case when @p_last is null then @p_min else @p_max - @p_last end
    
    select @p_min = case when @p_min2 > @p_min then @p_min2 else @p_min end
    select @p_max = case when @p_max2 < @p_max then @p_max2 else @p_max end
    

    that script sets up the parameters @p_min, @p_max, and @p_total as well as the temp table #results

    You can then use this to select actual data; select 2 table results, first one being metadata (select this first because the second table might not have any actual rows and your reader implementation might not be capable of dealing with that without backtracking):

    select [Count] = @p_total, 
        HasPreviousPage = (case when @p_min > 1 then 1 else 0 end),
        HasNextPage = (case when @p_max + 1 < @p_total then 1 else 0 end)
    

    followed by the paged window of results that you actually want:

    select [RowIndex], Col1, Col2, Col3 
    from #results where RowNumber between @p_min and @p_max
    

    Doing this generic solution permits the ability to expose whatever paging strategy you wish. You can do a streaming solution (facebook, google, stackoverflow, reddit, ...) via @p_after and @p_first (or @p_before and @p_last). You can do an offset + take with @p_first and @p_skipFirst. You can also do a page + size with the same parameters @p_first = size and @p_skipFirst = (page - 1) * size. Further you can do more esoteric paging strategies (last X pages, between absolute records, offset + anchor, etc) with other combinations of parameters.


    This said, Sybase (SAP) ASE does now directly support the offset + take strategy via rows limit @p_first offset @p_skipFirst. If you only wished to support that strategy you could simplify the above to:

    declare @p_total int
    select @p_total = count(*) from #resultsBeforeSort
    
    select [Count] = @p_total,
           [HasPreviousPage] = (case when @p_skipFirst > 0 then 1 else 0 end),
           [HasNextPage] = (case when @p_total > @p_skipFirst + @p_first then 1 else 0 end)
    
    select [RowIndex], Col1, Col2, Col3
      from #resultsBeforeSort
      order by Column1, Column2 desc, RowIndex
      rows limit @p_first offset @p_skipFirst
    

提交回复
热议问题