[Oracle10g] Requête permettant de grouper des élts sur une même ligne

Requête permettant de grouper des élts sur une même ligne [Oracle10g] - SQL/NoSQL - Programmation

Marsh Posté le 07-05-2008 à 11:09:15    

Bonjour,
 
je cherche à faire une requête dont la sortie permettrait d'afficher des éléments d'une collection de type VARCHAR2 groupés sur une même ligne.
 
Prenons un exemple simple, ça sera plus clair:
Si on a 2 tables :
Employe (id_employe, nom_employe) et JourDePresence (id_jour, libelle_jour, id_employe)
 
Le but de la requête est d'afficher la liste des employés avec leurs jours de présence, on fait donc:

Code :
  1. Select nom_employe, libelle_jour
  2.   from Employe emp , JourDePresence jour
  3.   where emp.id_employe= jour.id_employe;


Et cela nous donne quelque chose qui ressemble à ça :
 
nom_employe | libelle_jour
-----------------------------
Employé 1     | Lundi
Employé 1     | Mardi
Employé 2     | Mardi
Employé 2     | Mercredi
Employé 2     | Jeudi
etc...
 
Jusque là rien de plus simple.  
Mais dans mon cas, la requête renvoi un très grand nombre d'employés, or si on multiplie par le nombre de jours de présence, cela fait un fichier de sortie énorme et donc difficile à consulter. J'aimerais donc arriver à présenter la sortie sous la forme suivante :
 
nom_employe | jours
--------------------------------
Employé 1     | Lundi Mardi
Employé 2     | Mardi Mercredi Jeudi
etc...
 
En gros cela revient à faire un GROUP BY nom_employe, mais au lieu de faire un simple comptage des lignes groupées comme on pourrait le faire avec un COUNT, il faudrait arriver à concaténer les éléments de la colonne "libelle_jour" pour les afficher en face de chaque nom d'employé. Ceci aurait pour effet de limiter grandement le nombre de ligne en sortie et donc d'alléger le ficher de sortie.
 
Cela dit, je ne sais même pas si c'est possible, car cela revient à mettre plusieurs éléments dans une même "case" d'une colonne, et je ne sais pas si SQL le permet. Mais je me dis que comme les éléments sont de type VARCHAR, il y a peut-être moyen de les concaténer en ajoutant un espace de séparation. Mais je n'ai pas trouvé de fonction simple (comme le COUNT pour compter les lignes groupées) qui permette cet affichage, donc il y a 2 solutions :
 1 - Cette fonction existe, et on pourra dire que je ne sais pas chercher. [:befree]
 2 - Il faut contourner le problème, et là je sèche complètement.  :(  
 
Dans tout les cas merci d'avance pour vos conseils et idées.
 :jap:  
 
PS : pour info, la sortie se fait simplement par le spool sous la forme de fichiers CSV

Reply

Marsh Posté le 07-05-2008 à 11:09:15   

Reply

Marsh Posté le 07-05-2008 à 11:56:47    

a ma connaissance il va falloir passer par une stored procedure.
 
j'avais vu une fois passer que mysql avait une fonction pour ca style ws_concat mais oracle n'en dispose pas je pense

Reply

Marsh Posté le 07-05-2008 à 14:03:54    

Effectivement, je m'en suis sorti avec une fonction PL (un brin alambiquée).
Cela dit, si quelqu'un connait une manière de faire ça directement dans le select, je suis preneur !!! ;)
 
Pour info, au cas où cela intéresserait quelqu'un d'autre que moi, voilà comment j'ai fait :
 
Déclaration de la fonction:

Code :
  1. CREATE OR REPLACE FUNCTION LISTEJOURS (
  2. I_idemp  Employe.id_employe%TYPE
  3. ) RETURN VARCHAR2 IS
  4.   CURSOR curseur_jours(param_idemp NUMBER)IS
  5.     SELECT JP.libelle_jour
  6.     FROM JourDePresence JP
  7.     WHERE JP.id_employe= param_idemp;
  8.   retour   VARCHAR2(4000);
  9.   jourCourrant  VARCHAR2(50);
  10. BEGIN
  11.   retour:='';
  12.   FOR jourCourrant IN curseur_jours(I_idemp) LOOP
  13.     retour := substr(retour||' - '||jourCourrant ,1,4000);
  14.   END LOOP;
  15.   RETURN retour;
  16. END LISTEJOURS ;
  17. /


 
Appel de la fonction:

Code :
  1. SELECT nom_employe, LISTEJOURS(emp.id_employe) as jours
  2.   FROM Employe emp;


 
Bon, après cela peut encore être amélioré pour éviter le séparateur "-" en début de ligne, m'enfin ça fait ce qui était attendu.
Par contre on m'a dit que l'appel d'une fonction comme ça dans le SELECT pouvait ne pas passer car un SELECT ne modifie pas la base or la fonction pourrait modifier la base, et pour le permettre il fallait rajouter un tag PRAGMA dans la fonction. Mais là ça passe, donc tant mieux.
 
bon ben voilou
 :hello:


Message édité par masterslyp le 07-05-2008 à 14:19:26
Reply

Marsh Posté le 11-05-2008 à 17:16:18    

c'est des  conneries ce qu'on a t'a raconté. ça tournera très  bien comme cela.  
Le seul truc peut être, c'est que tu as besoin du droit EXECUTE sur la fonction en plus du droit de SELECT sur les tables.

Reply

Marsh Posté le 13-05-2008 à 10:13:47    

vu que tu n'updates rien tu n'auras pas de soucis, dans certains cas il faut effectivement ajouter un pragma de transaction autonome, mais pas si tu ne fais que des select.
 
moi c'est plus le substring qui me fait tiquer
 

Reply

Marsh Posté le 30-05-2008 à 14:24:44    

Désolé pour laps de temps assez énorme pour la réponse (j'avais perdu le tomic dans les méandre de hfr)
 
Effectivement, je n'ai aucun souci pour ma requête, ça passe très bien sans le PRAGMA.  
La réflexion qu'on m'avait fait vient du fait que comme on appelle une fonction dans le select, le contenu de cette fonction n'est pas forcément connu (genre dans un package où le body serait crypté), du coup si cette fonction fait un update, le select perdrait sans le savoir sa propriété qui est de ne pas toucher aux données.  
M'enfin je n'ai eu aucun souci là dessus donc je n'ai pas cherché plus loin se qu'il se passait si on appelait une fonction à update dans un select... :sleep:  
 
Pour le coup du substring, justement c'est pour éviter un dépassement de capacité de ma colonne qui reçoit la concaténation des jours. Certe dans l'exemple choisi on ne peut pas avoir plus de 7 jours, mais dans mon cas, cela aurait pu dépasser la taille du VARCHAR2 qui pour une colonne se trouve être 4000.

Reply

Sujets relatifs:

Leave a Replay

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