Question

De nombreuses applications disposent de grilles qui affichent les données d'une table de base de données, une page à la fois.Beaucoup d'entre eux permettent également à l'utilisateur de choisir le nombre d'enregistrements par page, de trier par n'importe quelle colonne et de naviguer d'avant en arrière dans les résultats.

Quel est un bon algorithme pour implémenter ce modèle sans apporter la table entière au client, puis filtrer les données sur le client.Comment apporter à l'utilisateur uniquement les enregistrements que vous souhaitez afficher ?

LINQ simplifie-t-il la solution ?

Était-ce utile?

La solution

Sur MS SQL Server 2005 et supérieur, ROW_NUMBER() ça a l'air de fonctionner :

T-SQL :Pagination avec ROW_NUMBER()

DECLARE @PageNum AS INT;
DECLARE @PageSize AS INT;
SET @PageNum = 2;
SET @PageSize = 10;

WITH OrdersRN AS
(
    SELECT ROW_NUMBER() OVER(ORDER BY OrderDate, OrderID) AS RowNum
          ,OrderID
          ,OrderDate
          ,CustomerID
          ,EmployeeID
      FROM dbo.Orders
)

SELECT * 
  FROM OrdersRN
 WHERE RowNum BETWEEN (@PageNum - 1) * @PageSize + 1 
                  AND @PageNum * @PageSize
 ORDER BY OrderDate
         ,OrderID;

Autres conseils

Je recommanderais soit d'utiliser LINQ, soit d'essayer de copier ce qu'il fait.J'ai une application dans laquelle j'utilise les méthodes LINQ Take et Skip pour récupérer des données paginées.Le code ressemble à ceci :

MyDataContext db = new MyDataContext();
var results = db.Products
    .Skip((pageNumber - 1) * pageSize)
    .Take(pageSize);

L'exécution de SQL Server Profiler révèle que LINQ convertit cette requête en SQL de la même manière :

SELECT [ProductId], [Name], [Cost], and so on...
FROM (
    SELECT [ProductId], [Name], [Cost], [ROW_NUMBER]
    FROM (
       SELECT ROW_NUMBER() OVER (ORDER BY [Name]) AS [ROW_NUMBER], 
           [ProductId], [Name], [Cost]
       FROM [Products]
    )
    WHERE [ROW_NUMBER] BETWEEN 10 AND 20
)
ORDER BY [ROW_NUMBER]

En anglais simple :
1.Filtrez vos lignes et utilisez la fonction ROW_NUMBER pour ajouter des numéros de ligne dans l'ordre souhaité.
2.Filtrez (1) pour renvoyer uniquement les numéros de ligne que vous souhaitez sur votre page.
3.Triez (2) par numéro de ligne, qui est le même que l'ordre souhaité (dans ce cas, par nom).

Il existe essentiellement deux façons de procéder à la pagination dans la base de données (je suppose que vous utilisez SQL Server) :

Utilisation du DÉCALAGE

D'autres ont expliqué comment le ROW_NUMBER() OVER() la fonction de classement peut être utilisée pour effectuer des pages.Il convient de mentionner que SQL Server 2012 inclut enfin la prise en charge du standard SQL. OFFSET .. FETCH clause:

SELECT first_name, last_name, score
FROM players
ORDER BY score DESC
OFFSET 40 ROWS FETCH NEXT 10 ROWS ONLY

Si vous utilisez SQL Server 2012 et que la compatibilité descendante n'est pas un problème, vous devriez probablement préférer cette clause car elle sera exécutée de manière plus optimale par SQL Server dans les cas extrêmes.

Utilisation de la méthode SEEK

Il existe une manière totalement différente, beaucoup plus rapide, mais moins connue, d'effectuer une pagination en SQL.C'est ce qu'on appelle souvent la « méthode de recherche », comme décrit dans ce billet de blog ici.

SELECT TOP 10 first_name, last_name, score
FROM players
WHERE (score < @previousScore)
   OR (score = @previousScore AND player_id < @previousPlayerId)
ORDER BY score DESC, player_id DESC

Le @previousScore et @previousPlayerId les valeurs sont les valeurs respectives du dernier enregistrement de la page précédente.Cela vous permet de récupérer la page "suivante".Si la ORDER BY la direction est ASC, utilisez simplement > plutôt.

Avec la méthode ci-dessus, vous ne pouvez pas passer immédiatement à la page 4 sans avoir d'abord récupéré les 40 enregistrements précédents.Mais souvent, on ne veut pas aller aussi loin de toute façon.Au lieu de cela, vous obtenez une requête beaucoup plus rapide qui pourrait récupérer des données en temps constant, en fonction de votre indexation.De plus, vos pages restent « stables », peu importe si les données sous-jacentes changent (par ex.à la page 1, alors que vous êtes à la page 4).

C'est le meilleur moyen d'implémenter la pagination lors du chargement paresseux de davantage de données dans des applications Web, par exemple.

Notez que la « méthode de recherche » est également appelée radiomessagerie par clavier.

LINQ combiné avec des expressions lambda et des classes anonymes dans .Net 3.5 extrêmement simplifie ce genre de chose.

Interrogation de la base de données :

var customers = from c in db.customers
                join p in db.purchases on c.CustomerID equals p.CustomerID
                where p.purchases > 5
                select c;

Nombre d'enregistrements par page :

customers = customers.Skip(pageNum * pageSize).Take(pageSize);

Tri par n'importe quelle colonne :

customers = customers.OrderBy(c => c.LastName);

Obtenir uniquement les champs sélectionnés du serveur :

var customers = from c in db.customers
                join p in db.purchases on c.CustomerID equals p.CustomerID
                where p.purchases > 5
                select new
                {
                    CustomerID = c.CustomerID,
                    FirstName = c.FirstName,
                    LastName = c.LastName
                };

Cela crée une classe anonyme typée statiquement dans laquelle vous pouvez accéder à ses propriétés :

var firstCustomer = customer.First();
int id = firstCustomer.CustomerID;

Les résultats des requêtes sont chargés par défaut, vous ne communiquez donc pas avec la base de données tant que vous n'avez pas réellement besoin des données.LINQ dans .Net simplifie également grandement les mises à jour en conservant un contexte de données de toutes les modifications que vous avez apportées et en mettant à jour uniquement les champs que vous modifiez.

Solution Oracle :

select * from (
    select a.*, rownum rnum from (
        YOUR_QUERY_GOES_HERE -- including the order by
    ) a
    where rownum <= MAX_ROW
 ) where rnum >= MIN_ROW

Il existe quelques solutions que j'utilise avec MS SQL 2005.

L'un d'eux est ROW_NUMBER().Mais, personnellement, je n'aime pas ROW_NUMBER() car cela ne fonctionne pas pour obtenir de gros résultats (la base de données sur laquelle je travaille est vraiment volumineuse - plus de 1 To de données exécutant des milliers de requêtes en une seconde - vous savez - un grand réseau social site).

Voici ma solution préférée.

J'utiliserai une sorte de pseudo-code de T-SQL.

Trouvons la 2ème page des utilisateurs triés par prénom, nom, où chaque page contient 10 enregistrements.

@page = 2 -- input parameter
@size = 10 -- can be optional input parameter

if @page < 1 then begin
    @page = 1 -- check page number
end
@start = (@page-1) * @size + 1 -- @page starts at record no @start

-- find the beginning of page @page
SELECT TOP (@start)
    @forename = forename,
    @surname = surname
    @id = id
FROM
    users
ORDER BY
    forename,
    surname,
    id -- to keep correct order in case of have two John Smith.

-- select @size records starting from @start
SELECT TOP (@size)
    id,
    forename,
    surname
FROM
    users
WHERE
    (forename = @forename and surname = @surname and id >= @id) -- the same name and surname, but bigger id
    OR (forename = @forename and surname > @surname) -- the same name, but bigger surname, id doesn't matter
    OR (forename > @forename) -- bigger forename, the rest doesn't matter
ORDER BY
    forename,
    surname,
    id

En fait, LINQ propose des méthodes Skip et Take qui peuvent être combinées pour choisir les enregistrements à récupérer.

Vérifiez-les.

Pour la base de données : Pagination dans SQL Server 2005

Il y a une discussion à ce sujet Ici

La technique obtient le numéro de page 100 000 à partir d'une base de données de 150 000 lignes en 78 ms

Grâce aux connaissances de l'optimiseur et à SET ROWCOUNT, le premier EmployeeID de la page demandée est stocké dans une variable locale comme point de départ.Ensuite, DÉFINISSEZ ROWCOUNT sur le nombre maximum d'enregistrements demandé dans @maximumRows.Cela permet de paginer l'ensemble de résultats de manière beaucoup plus efficace.L'utilisation de cette méthode tire également parti des index préexistants sur la table car elle accède directement à la table de base et non à une table créée localement.

J'ai bien peur de ne pas être en mesure de juger si c'est mieux que la réponse actuellement acceptée.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top