Article
· Jan 2 4m de lecture

Exemple de pagination avec ScrollableResultSet

Bonjour à toutes et à tous,

Ceci est un exemple complet d'utilisation d'un %ScrollableResultSet pour la pagination des résultats à l'aide de %DynamicQuery:SQL et créer une réponse JSON incluant les détails de la page.

N'hésitez pas à l'utiliser, le partager, le commenter ou l'améliorer.

Cordialement

ClassMethod getPersonsPag(iAge As %Integer, sortField As %String = 1, sortOrder As %String = 2, pageSize As %String = 20, pageIndex As %String = 1) As %DynamicObject
{
    set out = []
    set vFrom = ((pageIndex -1 ) * pageSize)+1
    set vTo = vFrom + (pageSize-1)

    set sql = "SELECT ID,SSN,LastName,GivenName,SecondaryName, Gender, Age "_
              "FROM Sample.Person WHERE Age > ? "_
              "Order By "_sortField _" "_ $CASE(sortOrder,1:"ASC",2:"DESC",:"ASC")
   
    Set rs=##class(%ScrollableResultSet).%New("%DynamicQuery:SQL")
    set sc = rs.Prepare(sql)
    set sc = rs.Execute(iAge) If $$$ISERR(sc) Do DisplayError^%apiOBJ(sc) Quit

    if (rs.Count()=0) Quit {"msg" : "No Records Found"}

    Do rs.CurrRowSet(vFrom)
    if pageSize >= rs.Count() set pageSize = rs.Count()

        try{
            FOR i=1:1:pageSize{
             Do out.%Push({
                "index": (i),
                "pid": (rs.%Get("ID")),
                "ssn" : (rs.%Get("SSN")),
                "lastname" : (rs.%Get("LastName")) ,
                "givenname":    (rs.%Get("GivenName")),
                "secondaryname":       (rs.%Get("SecondaryName")) ,
                "gender": (rs.%Get("Gender")),
                "age": (rs.%Get("Age") )
                })    
                Do rs.%Next()      
            }
         }
            catch(e){ }
    set outJson = []
    Do outJson.%Push({
                "pageSize":(pageSize),
                "pageIndex":(pageIndex),
                "fromIndex":(vFrom),
                "toIndex":(vFrom+i - 1),
                "resultSetTotal":(rs.Count()),
                "pageRecords":(i),
                "pages":($NORMALIZE((rs.Count()/pageSize),0)),
                "resultSet":(out)
                })
    return outJson

}
Discussion (4)1
Connectez-vous ou inscrivez-vous pour continuer

@Guillaume Rongier ajoute :

Bonjour Rubén,

Une autre proposition sur IRIS 2021.1+ peut être celle-ci avec l'utilisation de la fonction nouvelle fenêtre (OVER) :

ClassMethod getPersonsPagWindow(iAge As %Integer, sortField As %String = 1, sortOrder As %String = 2, pageSize As %String = 20, pageIndex As %String = 1) As %DynamicObject
{
    set out = []
    set vFrom = ((pageIndex -1 ) * pageSize)+1
    set vTo = vFrom + (pageSize-1)

    set sql = "SELECT * "_
                "FROM ( SELECT persons.* "_
                "        , ROW_NUMBER() OVER (ORDER By "_sortField_" "_ $CASE(sortOrder,1:"ASC",2:"DESC",:"ASC")_
                "    ) rn "_
                "        FROM Sample.Person persons where Age > ? "_
                "    ) tmp "_
                "WHERE rn between "_vFrom_" and "_vTo_" "_
                "ORDER By "_sortField_" "_ $CASE(sortOrder,1:"ASC",2:"DESC",:"ASC")

    Set rs=##class(%ResultSet).%New("%DynamicQuery:SQL")
    set sc = rs.Prepare(sql)
    set sc = rs.Execute(iAge) If $$$ISERR(sc) Do DisplayError^%apiOBJ(sc) Quit

    while rs.%Next() {
        Do out.%Push({
                "pid": (rs.%Get("ID")),
                "ssn" : (rs.%Get("SSN")),
                "lastname" : (rs.%Get("LastName")) ,
                "givenname":    (rs.%Get("GivenName")),
                "secondaryname":       (rs.%Get("SecondaryName")) ,
                "gender": (rs.%Get("Gender")),
                "age": (rs.%Get("Age") )
                })
    }

    set outJson = []
    Do outJson.%Push({
                "pageSize":(pageSize),
                "pageIndex":(pageIndex),
                "fromIndex":(vFrom),
                "toIndex":(vTo),
                "resultSet":(out)
                })
    return outJson
}

Je compare les deux solutions sur un dataset de 100 000 lignes sans index avec un résultat de 20 éléments en page 1 et voici les résultats :

"getPersonsPag timed : 1,647 secondes"
"getPersonsPagWindow timed : 0,247 secondes"

Je suppose que la fonction window est plus rapide car vous n'avez pas besoin de récupérer toutes les données de manière globale avant la pagination.

@Eduard Lebedyuk précise :

L'idée d'un ensemble de résultats défilants est d'appeler Save/OpenId - et l'ensemble de résultats continuera automatiquement sur une ligne suivante. Vous n'avez donc pas besoin de gérer les indices avant/arrière :

 
Un exemple ici

C'est également environ 3 fois plus rapide puisque la requête n'est exécutée qu'une seule fois : 

do ##class(User.Pagination).Time("Save")
Save took 0,0048 sec
do ##class(User.Pagination).Time("NoSave")
NoSave took 0,0143 sec