[ORACLE] Pagination et tri (order by)

Pagination et tri (order by) [ORACLE] - SQL/NoSQL - Programmation

Marsh Posté le 22-05-2007 à 17:49:41    

J'ai un gros problème, je suis débutant sous oracle, et j'ai besoin de faire une liste paginée avec un tri sur chaque colonne.

 

Ca fait des heures que je cherche comment effectuer cette pagination en utilisant des clauses order by group by dans mes requetes, mais après pas mal de recherches, je ne trouve rien qui marche.

 

Dès que je tri ma liste, les elements de la nième page ne sont plus les mêmes, et je ne peux pas faire de order by rowid a cause de mes clauses group by...

 

Pour info voila la tête de ma requete :

 
Code :
  1. SELECT  GROUPE,
  2.  REPLACE(NOM_SIEGE,'*') as NOM,
  3.  SUM(NB) AS PERS_RAPPR,
  4.  SUM(VS) as VS,
  5.  SUM(VG) as VG,
  6.  //etc
  7. FROM SIEGES,
  8.  (
  9.   SELECT COUNT(*) as NB,
  10.    SUM(decode (RAPM_CODE_VIGI , 'VS', 1 , 'VG', 0, 'VC', 0, 'VV', 0, '', 0)) AS VS,
  11.    SUM(decode (RAPM_CODE_VIGI , 'VG', 1 , 'VS', 0, 'VC', 0, 'VV', 0, '', 0)) AS VG,
  12.    //etc
  13.                         LPAD(RAPM_GROUPE,5,'0') as GROUPE
  14.   from RAPPRO_PERS_MORALE ,
  15.                                LNK_RAPM_TRR 
  16.   WHERE 1=1 
  17.   AND RAPM_DR =08040
  18.   AND LNK_RAPM_TRR.RAPM_ID_PM = RAPPRO_PERS_MORALE.RAPM_ID_PM
  19.   GROUP BY RAPM_GROUPE
  20.   union all     
  21.   SELECT COUNT(*),
  22.    SUM(decode (VGPP_PART_VIGI , 'VS', 1 , 'VG', 0, 'VC', 0, 'VV', 0, '', 0)) AS VS,
  23.    SUM(decode (VGPP_PART_VIGI , 'VG', 1 , 'VS', 0, 'VC', 0, 'VV', 0, '', 0)) AS VG,
  24.    //etc
  25.    0,
  26.    0,
  27.    0,
  28.    LPAD (VGPP_GROUPE,5,'0') as GROUPE
  29.   from vigilance 
  30.   WHERE 1=1 
  31.   AND VGPP_DR =08040
  32.   GROUP BY VGPP_GROUPE,
  33.   VGPP_PART_VIGI)
  34. WHERE GROUPE = SIEGES.CODE_SIEGE 
  35. GROUP BY GROUPE,NOM_SIEGE order by nom DESC
 

J'ai essayé d'appliquer cette méthode (ou ci-dessous) mais sans succès, la pagination marche (50 par 50 ici), mais je ne peux pas me servir des critères de tri (order by n'importe quel champ du 1er select bien sur) car oracle refuse le order by rowid sur un group by :/

Code :
  1. select *
  2.   from ( select /*+ FIRST_ROWS(n) */
  3.   a.*, ROWNUM rnum
  4.       from ( ma_requete,
  5.       order by un_champ, rowid) a
  6.       where ROWNUM <= 150
  7. where rnum  >= 100;
 

Chaque fleche sur la photo ci dessous tri la liste par ordre croissant ou decroissant de nom, vs ou autre...
Si je pagine la liste, et que je n'utilise pas les critères de tri, je n'ai pas besoin de faire un order by rowid apparement d'apres le lien ou j'ai trouvé la solution de pagination
Par contre si je tri sans mettre ce 'order by rowid', par exemple je me positionne sur la page 1 (le nom des lignes  va de 'a' à 'h', si je tri par nom desc je n'aurait pas les ligne inversées (de 'h' à 'a' ) mais de 'z' à ' r', le tri ne se fera pas que sur les éléments de la page 1 mais sur toute la liste comme s'il elle n'était pas paginée et donc les élements de ma page 1 après le tri n'ont plus rien avoir avec ce qu'il était avant.

 

http://img502.imageshack.us/img502/488/hfrid6.jpg

 

Si vous pouviez me donner un coup de main ça me sauverai car je suis dans une impasse :cry:, je pense qu'il y a un moyen d'implémenter ce qui est écris dans le lien, mais je n'y parviens pas.

 

EDIT : Désolé d'avoir trafiqué le screen, données sensibles oblige :/


Message édité par Alisteroid le 22-05-2007 à 19:11:24
Reply

Marsh Posté le 22-05-2007 à 17:49:41   

Reply

Marsh Posté le 22-05-2007 à 19:03:10    

OK, je viens de piger ton problème.
 
Il faut dire que c'est assez étrange ton truc : normalement, le tri fonctionne comme tu ne veux pas. En tout cas, j'ai toujours vu faire comme ça ;)
 
Effectivement, la pagination se fait une fois les données triées, et non l'inverse.
 
Toi, ce que tu semble vouloir faire, c'est trier une seconde fois le résultat, pour afficher les données paginées selon un autre tri que le tri principal...
En soit, c'est un peu bordelique, dans la mesure ou l'utilisateur doit spécifier deux tris distincts, à moins que celui "global" ne soit "en dur".
 
Bon, je vais voir ce que je peux faire. A mon avis, c'est très simplement résolvable avec deux order by : un au niveau de ta sous-requête, et un second ensuite dans la requête finale... Mais cela demande un niveau supplémentaire d'imbrication. J'espère qu'Oracle va comprendre ton idée farfelue ;)

Reply

Marsh Posté le 22-05-2007 à 19:05:09    

Dans mon test, je vais faire ceci :
 
Requête principale : recherche des commandes d'une base, avec comme informations : numéro de commande, nombre de lignes de la commande, et montant total de la commande
 
Premier tri permettant de trier mes pages par "numéro de commande".
Second tri permettant de trier l'intérieur de ma pase par "nombre de produits"

Reply

Marsh Posté le 22-05-2007 à 19:12:24    

Merci beaucoup MagicBuzz :jap:, je crois que je suis pas sorti du taf avant  minuit :D

Reply

Marsh Posté le 22-05-2007 à 19:12:41    

Ben en fait, c'est tout peanuts, même pas besoin de rajouter un niveau :

Code :
  1. SELECT numeve, nblig, montant
  2. FROM
  3. (
  4.     SELECT rownum ligne, numeve, nblig, montant
  5.     FROM
  6.     (
  7.         SELECT eve.numeve, count(*) nblig, sum(evp.qtecde * evp.prxvdu) montant
  8.         FROM eve
  9.         INNER JOIN evp ON evp.codsoc = eve.codsoc AND evp.achvte = eve.achvte AND evp.numeve = eve.numeve
  10.         WHERE eve.codsoc = 2
  11.         AND eve.achvte = 'V'
  12.         AND eve.typeve = 'CDV'
  13.         AND eve.dateve LIKE '200705%'
  14.         GROUP BY eve.numeve
  15.         ORDER BY numeve
  16.     )
  17.     WHERE rownum < 150
  18. )
  19. WHERE ligne > 140
  20. ORDER BY nblig


 
Explication :

Code :
  1. SELECT eve.numeve, count(*) nblig, sum(evp.qtecde * evp.prxvdu) montant
  2. FROM eve
  3. INNER JOIN evp ON evp.codsoc = eve.codsoc AND evp.achvte = eve.achvte AND evp.numeve = eve.numeve
  4. WHERE eve.codsoc = 2
  5. AND eve.achvte = 'V'
  6. AND eve.typeve = 'CDV'
  7. AND eve.dateve LIKE '200705%'
  8. GROUP BY eve.numeve
  9. ORDER BY numeve


 
Recherche des informations sur les commandes du mois de mai triées par numéro de commandes.
 

Code :
  1. SELECT rownum ligne, numeve, nblig, montant
  2. FROM
  3. (
  4.     SELECT eve.numeve, count(*) nblig, sum(evp.qtecde * evp.prxvdu) montant
  5.     FROM eve
  6.     INNER JOIN evp ON evp.codsoc = eve.codsoc AND evp.achvte = eve.achvte AND evp.numeve = eve.numeve
  7.     WHERE eve.codsoc = 2
  8.     AND eve.achvte = 'V'
  9.     AND eve.typeve = 'CDV'
  10.     AND eve.dateve LIKE '200705%'
  11.     GROUP BY eve.numeve
  12.     ORDER BY numeve
  13. )
  14. WHERE rownum < 150


 
On ne garde que les 150 premières commandes.
 

Code :
  1. SELECT numeve, nblig, montant
  2. FROM
  3. (
  4.     SELECT rownum ligne, numeve, nblig, montant
  5.     FROM
  6.     (
  7.         SELECT eve.numeve, count(*) nblig, sum(evp.qtecde * evp.prxvdu) montant
  8.         FROM eve
  9.         INNER JOIN evp ON evp.codsoc = eve.codsoc AND evp.achvte = eve.achvte AND evp.numeve = eve.numeve
  10.         WHERE eve.codsoc = 2
  11.         AND eve.achvte = 'V'
  12.         AND eve.typeve = 'CDV'
  13.         AND eve.dateve LIKE '200705%'
  14.         GROUP BY eve.numeve
  15.         ORDER BY numeve
  16.     )
  17.     WHERE rownum < 150
  18. )
  19. WHERE ligne > 140
  20. ORDER BY nblig


 
On ne prends parmis que les 150 sélectionnées qu'à partir de 140, en triant cette fois par nblignes.
Et ça marche :p

Reply

Marsh Posté le 22-05-2007 à 19:13:05    

D'autres questions ? :D

Reply

Marsh Posté le 22-05-2007 à 19:38:57    

T'es un dieu :D
 
C'était super con, merci encore et encore pour ta patience et ta sympathie :jap:
 
EDIT: J'ai bien testé et ça marche nickel  [:dawak]


Message édité par Alisteroid le 22-05-2007 à 19:39:35
Reply

Marsh Posté le 22-05-2007 à 20:33:58    

Ahhhhhhhhh comment je fais pour avoir le nombre de lignes qu'aurait retourné la requete sans la pagination?

Reply

Marsh Posté le 23-05-2007 à 07:22:10    

Bon je suis désolé MagicBuzz, mais je me rends compte que ce que je voulais faire est compltement débile , ça doit être la fatigue, je me retrouve à ne plus pouvoir trier ma liste entierement mais uniquement page -> sans intéret.

 

Etant donné que de limiter le nombre d'enregistrement revient au même que de tout prendre au niveau perf, autant que je fasse un array_slice() sur mon tableau avant l'affichage (enfin je crois, a moins que tu connaisses une bonne syntaxe pour gagner en perf, mais pour l'instant je vois aucune différence entre paginer 10 par 10 ou afficher 400 d'un coup) .


Message édité par Alisteroid le 23-05-2007 à 09:36:40
Reply

Marsh Posté le 23-05-2007 à 09:38:04    

Le souci, c'est que si demain tu passes à une architecture n-Tiers (SGBD et application sur deux serveurs distincts) tu vas avoir un problème de bande passante entre les deux.
 
Effectivement, à un pouillème prêt, en local, récupérer 10 lignes dans une requête, ou 100 000 lignes, ça change pas grand chose (faut quand même penser à l'occupation mémoire qui se prends une sérieuse claque mais bon).
 
Par contre, quand les 10 ou 100 000 lignes passent à travers une connexion réseau, même en 1 Gbps, y'a une réelle différence.
 
Bon, après, passer de 10 à 400, c'est moins parlant, d'autant que si les lignes sont pas trop grosses, si ça se trouve ça va même pas générer de trame supplémentaire :D
 
Sinon, après c'est en fonction du langage d'interrogation.
Avec ADODB, on peut faire un select sans filtre (donc 100 000 lignes) qui restent sur le serveur, récupérer le nombre de lignes, puis récupérer sur le client uniquement les lignes "140 à 150". Je te laisse chercher dans la doc de ton langage (PHP ?) pour voir si tu peux reproduire un tel comportement.
 
En gros, faut que tu regardes s'il existe des "recordset serveurs".

Reply

Marsh Posté le 23-05-2007 à 09:38:04   

Reply

Marsh Posté le 23-05-2007 à 09:47:52    

Mouais, je vois , ma base est assez légère (30Mo) et au pire je fais un calcul sur pratiquement (sum) tout mais le résultat de ma requete ne me retourne au pire que 500 lignes, et seulement une dixaine de champs.
 
D'après ce que tu viens de me dire, je doute qu'il soit utile de me prendre la tête.

Reply

Marsh Posté le 23-05-2007 à 09:48:11    

Merci beaucoup de ton explication en tout cas ;)

Reply

Sujets relatifs:

Leave a Replay

Make sure you enter the(*)required information where indicate.HTML code is not allowed