• RSS
  • Delicious
  • Digg
  • Facebook
  • Twitter
  • Linkedin
  • Youtube

SQL :: Paginarea rezultatelor

AlexB marți, 6 martie 2012, 19:43 10 comentarii

In multe aplicatii este nevoie sa sa se afiseze paginat rezultatele dintr-un tabel din baza de date ordonate in functie de un anumit criteriu (frecvent: Nume, Data sau Pret).

Pentru a fi mai usor, sa luam un exemplu simplu: Presupunem ca avem urmatorul tabel:

Product
——————————–
IDProduct int
IDCategory int
ProductName nvarchar(200)

Sa presupunem ca dorim sa facem o paginare: cate 10 produse pe pagina, ordonate alfabetic.

Care ar fi query-ul care selecteaza produsele din categoria cu id-ul 27,  pentru pagina a doua, de exemplu?

Pe SQL Server 2008 fac asa (observatie. In coloana RowsCount voi primi numarul total de rezultate. Util pentru paginator, stiti voi: “pagina 2 din 1127”):

SELECT * FROM
(
    SELECT
        ROW_NUMBER() OVER (ORDER BY ProductName ASC) as RowNumber,
        COUNT(*) OVER () as RowsCount,
        IDProduct,
        ProductName
    FROM
        Product
    WHERE
        IDCategory = 27
) as t
WHERE
    RowNumber BETWEEN 21 AND 30

Pe SQL Server 2000 faceam asa:

SELECT TOP 10
    IDProduct,
    ProductName
FROM
    Product
WHERE
    IDCategory = 27 AND
    IDProduct not in
    (               
        SELECT TOP 20
            IdProduct
        FROM
            Product
        ORDER BY
            ProductName
    )
ORDER BY
    ProductName

Intrebarea mea, pentru ‘cestia mai specialisti in SQL este: Voi cum faceti treaba asta? Care din varianta o preferati? Exista variante mai bune?

PS. Stiu, pe mysql se face asa:

SELECT 
	IDProduct, 
	ProductName 
FROM
	Product 
WHERE 
	IDCategory = 27 
ORDER BY
	ProductName 
LIMIT 21, 10

Dar asta e prea simplu, nu-i asa? :D

   

Comentarii Facebook

10 comentarii pana acum. Si tu poti sa comentezi!

  1. Sandor spune:

    Articol bun. In SQL 2000 mi se pare complicata sintaxa .

    La MySQL este o mica greseala, codul actual afiseaza 30 de produse in loc de 10.

    Ultima linie trebuie sa fie:
    LIMIT 21, 10

    ps: 21 este offset-ul, 10 este nr-ul de produse.

  2. AlexB spune:

    Multam, multam fain! Eu de cate ori am de facut ceva in mysql si/sau php tot trebuie sa fiu cu documentatia langa mine, ca mi se pare un pic peste mana. :)

  3. Carmen spune:

    Pe SQL Server 2012:

    SELECT IDProduct, ProductName
    FROM Product
    ORDER BY ProductName
    OFFSET 10 ROWS
    FETCH NEXT 10 ROWS ONLY

    Dragut, asa-i? Mai mult, poti folosi variabile, expresii si subqueries dupa OFFSET si FETCH. :)

  4. AlexB spune:

    Fain, fain, fata faina! Multam fain, ţucu-te!

  5. AlexB spune:

    De pe facebook, de pe un grup de programatori:

    Alex Mang:

    Ca raspuns intrebarii tale, nu as folosi niciuna dintre variante propuse. Lasand deoparte configurarea index-ilor, ar fi mult mai performant sa implementezi un CTE din care sa faci select-ul ulterior.

    Altfel, imediat dupa index scan-ul de pe cheia primara vei avea segmentari si spool-uri consecutive. Mai pe romaneste, Select-ul in Select este bun doar in facultate. In real-life, evita-l!

    Ca propunere rapida dar neoptimizata, uite cum ai putea sa scrii query-ul cu CTE:

    WITH TopCTE AS
    (
    SELECT TOP 30000
    ROW_NUMBER() OVER(ORDER BY ProductName) AS RowNumber, ProductName
    FROM Product
    ORDER BY ProductName
    )
    SELECT *
    FROM TopCTE
    WHERE RowNumber > 29990

    Un common table expression nu este un subselect, fiindca nu se executa pentru fiecare inregistrare in parte si asadar nu rezulta acele spool-uri urate, ci se creaza un subset intr-o memorie tampon asupra caruia se realizeaza urmatoarea interogare.

    MSDN are o documentatie relativ buna asupra CTE-urilor http://msdn.microsoft.com/en-us/library/ms190766.aspx

  6. Dani Pop spune:

    Loool, ce bălării îs alea în SQL Server 2000 şi 2008, mă?! Eu nu vă înţeleg la ce bun lucraţi cu prostiile alea când există variante mult mai simple.

  7. Dani Pop spune:

    Oh, Carmen, ai salvat lumea microsoftiştilor de la veşnică ocară! :)

    Apropo, varianta în SQL Server 2012 seamănă dubios de mult cu cea din MySQL. Asta spune ceva.

  8. AlexB spune:

    Spune mai mult decat faptul ca site-ul oficial php este http://www.php.net (in libera citire php dot net)?

  9. Manu spune:

    Mai se poate si cu temp tables:
    SELECT ROW_NUMBER() OVER(ORDER BY col) AS RowID,
    col1,
    …,
    colN
    INTO #tmp_paging
    WHERE cond;
    SELECT * FROM #tmp_paging WHERE RowID BETWEEN limInf and limSup

  10. Manu spune:

    Sau folosind din .NET cu LINQ(nu numai linq 2 sql):

    from val in ctx.table
    Skip((pageIndex – 1) * pageSize).
    Take(pageSize);