web-dev-qa-db-fra.com

Faire pivoter des lignes dans des colonnes de manière dynamique dans Oracle

J'ai la table Oracle 10g suivante appelée _kv:

select * from _kv

ID       K       V
----     -----   -----
  1      name    Bob
  1      age     30
  1      gender  male
  2      name    Susan
  2      status  married

Je voudrais transformer mes clés en colonnes en utilisant du SQL pur (pas de PL/SQL) afin que la table résultante ressemble à ceci:

ID       NAME    AGE    GENDER  STATUS
----     -----   -----  ------  --------
  1      Bob      30     male 
  2      Susan                   married
  • La requête doit avoir autant de colonnes qu'il y a de Ks uniques dans la table (il n'y en a pas beaucoup)
  • Il n'y a aucun moyen de savoir quelles colonnes peuvent exister avant d'exécuter la requête.
  • J'essaie d'éviter d'exécuter une requête initiale pour générer par programme la requête finale.
  • Les cellules vides peuvent être des zéros ou des chaînes vides, cela n'a pas d'importance.
  • J'utilise Oracle 10g, mais une solution 11g serait également acceptable.

Il existe de nombreux exemples pour savoir quand vous savez comment s'appellent vos colonnes pivotantes, mais je ne trouve pas de solution générique pour Oracle.

Merci!

22
ojosilva

Oracle 11g fournit une opération PIVOT qui fait ce que vous voulez.

Solution Oracle 11g

select * from
(select id, k, v from _kv) 
pivot(max(v) for k in ('name', 'age', 'gender', 'status')

(Remarque: je n'ai pas de copie de 11g pour le tester, je n'ai donc pas vérifié sa fonctionnalité))}

J'ai obtenu cette solution de: http://orafaq.com/wiki/PIVOT

EDIT - option pivot xml (également Oracle 11g)
Apparemment, il existe également une option pivot xml pour les cas où vous ne connaissez pas tous les en-têtes de colonnes dont vous pourriez avoir besoin. (voir la section XML TYPE vers le bas de la page située à http://www.Oracle.com/technetwork/articles/sql/11g-pivot-097235.html )

select * from
(select id, k, v from _kv) 
pivot xml (max(v)
for k in (any) )

(Remarque: comme auparavant, je n'ai pas de copie de 11g pour le tester, je n'ai donc pas vérifié sa fonctionnalité))

Edit2: Changé v dans les instructions pivot et pivot xml en max(v) car il est censé être agrégé comme indiqué dans l'un des commentaires. J'ai également ajouté la clause in qui n'est pas facultative pour pivot. Bien entendu, le fait de devoir spécifier les valeurs dans la clause in va à l’encontre de l’objectif consistant à avoir une requête pivot/tableau croisé complètement dynamique, comme le souhaitait l’affiche de cette question. 

29
dave

Pour traiter des situations dans lesquelles il est possible que plusieurs valeurs soient utilisées (v dans votre exemple), j'utilise PIVOT et LISTAGG:

SELECT * FROM
(
  SELECT id, k, v
  FROM _kv 
)
PIVOT 
(
  LISTAGG(v ,',') 
  WITHIN GROUP (ORDER BY k) 
  FOR k IN ('name', 'age','gender','status')
)
ORDER BY id;

Puisque vous voulez des valeurs dynamiques, utilisez SQL dynamique et transmettez les valeurs déterminées en exécutant une sélection sur les données de la table avant d'appeler l'instruction pivot.

6
eniacAvenger

Arrive pour avoir une tâche sur pivot. Ci-dessous fonctionne pour moi comme testé tout à l'heure sur 11g:

select * from
(
  select ID, COUNTRY_NAME, TOTAL_COUNT from ONE_TABLE 
) 
pivot(
  SUM(TOTAL_COUNT) for COUNTRY_NAME in (
    'Canada', 'USA', 'Mexico'
  )
);
5
Herbert Yu

Tout d’abord, il faut analyser de nouveau le pivot dynamique en utilisant pivot xml. Nous avons un autre moyen de le faire en stockant les noms de colonne dans une variable et en les transmettant dans le SQL dynamique comme ci-dessous.

Considérons que nous avons un tableau comme ci-dessous.

 enter image description here

Si nous devons afficher les valeurs de la colonne YR en tant que noms de colonnes et les valeurs de ces colonnes à partir de QTY, nous pouvons utiliser le code ci-dessous.

declare
  sqlqry clob;
  cols clob;
begin
  select listagg('''' || YR || ''' as "' || YR || '"', ',') within group (order by YR)
  into   cols
  from   (select distinct YR from EMPLOYEE);


  sqlqry :=
  '      
  select * from
  (
      select *
      from EMPLOYEE
  )
  pivot
  (
    MIN(QTY) for YR in (' || cols  || ')
  )';

  execute immediate sqlqry;
end;
/

RÉSULTAT 

 enter image description here

1
Sarath Avanavu