web-dev-qa-db-fra.com

Pivot dynamique dans Oracle SQL

... pivot (somme (A) pour B dans (X)) 

Maintenant, B est du type de données varchar2 et X est une chaîne de valeurs varchar2 séparées par des virgules.
Les valeurs pour X sont des valeurs sélectionnées distinctes d’une colonne (disons CL) de la même table. De cette façon, la requête pivot fonctionnait. 

Mais le problème est que chaque fois qu'il y a une nouvelle valeur dans la colonne CL, je dois l'ajouter manuellement à la chaîne X.

J'ai essayé de remplacer X par certaines valeurs distinctes de CL. Mais la requête n'est pas en cours d'exécution.
Je pensais que c'était dû au fait que, pour remplacer X, nous avons besoin de valeurs séparées par des virgules.
Ensuite, j’ai créé une fonction qui renvoie exactement le résultat à la chaîne X. Mais la requête ne s’exécute toujours pas.
Les messages d'erreur affichés ressemblent à "manquant de parenthèses droites", "canal de communication de fin de fichier", etc.
J'ai essayé pivot xml au lieu de simplement pivot, la requête s'exécute mais donne des vlaues comme oraxxx, etc., qui ne contiennent aucune valeur. 

Peut-être que je ne l'utilise pas correctement.
Pouvez-vous me dire une méthode pour créer un pivot avec des valeurs dynamiques?

10
prabhakar

Vous ne pouvez pas insérer de chaîne non constante dans la clause IN de la clause pivot.
Vous pouvez utiliser Pivot XML pour cela.

De documentation :

sous-requête Une sous-requête est utilisée uniquement avec le mot-clé XML . Lorsque vous spécifiez une sous-requête, toutes les valeurs trouvées par la sous-requête sont utilisées pour pivoter

Ça devrait ressembler à ça:

select xmlserialize(content t.B_XML) from t_aa
pivot xml(
sum(A) for B in(any)
) t;

Vous pouvez également avoir une sous-requête au lieu du mot clé ANY:

select xmlserialize(content t.B_XML) from t_aa
pivot xml(
sum(A) for B in (select cl from t_bb)
) t;

Voici une démo de sqlfiddle

8
A.B.Cade

Vous ne pouvez pas insérer une instruction dynamique dans l'instruction IN du PIVOT sans utiliser XML PIVOT, qui génère une sortie moins que souhaitable. Cependant, vous pouvez créer une chaîne IN et la saisir dans votre instruction.

Premièrement, voici mon exemple de table;

  myNumber    myValue myLetter
---------- ---------- --------
         1          2 A        
         1          4 B        
         2          6 C        
         2          8 A        
         2         10 B        
         3         12 C        
         3         14 A      

Commencez par configurer la chaîne à utiliser dans votre instruction IN. Ici, vous mettez la chaîne dans "str_in_statement". Nous utilisons COLUMN NEW_VALUE et LISTAGG pour configurer la chaîne.

clear columns
COLUMN temp_in_statement new_value str_in_statement
SELECT DISTINCT 
    LISTAGG('''' || myLetter || ''' AS ' || myLetter,',')
        WITHIN GROUP (ORDER BY myLetter) AS temp_in_statement 
    FROM (SELECT DISTINCT myLetter FROM myTable);

Votre chaîne ressemblera à:

'A' AS A,'B' AS B,'C' AS C

Maintenant, utilisez l'instruction String dans votre requête PIVOT.

SELECT * FROM 
    (SELECT myNumber, myLetter, myValue FROM myTable)
    PIVOT (Sum(myValue) AS val FOR myLetter IN (&str_in_statement));

Voici la sortie:

  MYNUMBER      A_VAL      B_VAL      C_VAL
---------- ---------- ---------- ----------
         1          2          4            
         2          8         10          6 
         3         14                    12 

Il y a cependant des limites. Vous ne pouvez concaténer qu'une chaîne de 4 000 octets maximum. 

18
user2179919

Pour les lecteurs ultérieurs, voici une autre solution https://technology.amis.nl/2006/05/24/dynamic-sql-pivoting-stiv-stealing-antons-thunder/

permettant une requête comme

select * from table( pivot(  'select deptno,  job, count(*) c from scott.emp group by deptno,job' ) )
4
Scott

UTILISER LA RECHERCHE DYNAMIQUE  

Le code de test est ci-dessous


--  DDL for Table TMP_TEST
--------------------------------------------------------

  CREATE TABLE "TMP_TEST" 
   (    "NAME" VARCHAR2(20), 
    "APP" VARCHAR2(20)
   );
/
SET DEFINE OFF;
Insert into TMP_TEST (NAME,APP) values ('suhaib','2');
Insert into TMP_TEST (NAME,APP) values ('suhaib','1');
Insert into TMP_TEST (NAME,APP) values ('shahzad','3');
Insert into TMP_TEST (NAME,APP) values ('shahzad','2');
Insert into TMP_TEST (NAME,APP) values ('shahzad','5');
Insert into TMP_TEST (NAME,APP) values ('tariq','1');
Insert into TMP_TEST (NAME,APP) values ('tariq','2');
Insert into TMP_TEST (NAME,APP) values ('tariq','6');
Insert into TMP_TEST (NAME,APP) values ('tariq','4');
/
  CREATE TABLE "TMP_TESTAPP" 
   (    "APP" VARCHAR2(20)
   );

SET DEFINE OFF;
Insert into TMP_TESTAPP (APP) values ('1');
Insert into TMP_TESTAPP (APP) values ('2');
Insert into TMP_TESTAPP (APP) values ('3');
Insert into TMP_TESTAPP (APP) values ('4');
Insert into TMP_TESTAPP (APP) values ('5');
Insert into TMP_TESTAPP (APP) values ('6');
/
create or replace PROCEDURE temp_test(
  pcursor out sys_refcursor,
    PRESULT                   OUT VARCHAR2
    )
AS
V_VALUES VARCHAR2(4000);
V_QUERY VARCHAR2(4000);
BEGIN
 PRESULT := 'Nothing';

-- concating activities name using comma, replace "'" with "''" because we will use it in dynamic query so "'" can effect query.
  SELECT DISTINCT 
         LISTAGG('''' || REPLACE(APP,'''','''''') || '''',',')
         WITHIN GROUP (ORDER BY APP) AS temp_in_statement 
    INTO V_VALUES
    FROM (SELECT DISTINCT APP 
            FROM TMP_TESTAPP);

-- designing dynamic query  

  V_QUERY := 'select * 
                from (  select NAME,APP 
                          from TMP_TEST   )   
               pivot (count(*) for APP in 
                     (' ||V_VALUES|| '))  
           order  by NAME' ;

    OPEN PCURSOR
     FOR V_QUERY;


 PRESULT := 'Success';

Exception
WHEN OTHERS THEN
 PRESULT := SQLcode || ' - ' || SQLERRM;
END temp_test;
2
Rana Suhaib

J'ai utilisé la méthode ci-dessus (fonction personnalisée pivot Anton (PL/SQL) Anton ()) et le travail a été fait! Comme je ne suis pas un développeur Oracle professionnel, voici les étapes simples que j'ai effectuées:

1) Téléchargez le paquet Zip pour y trouver le fichier pivotFun.sql . 2) Exécutez une fois le fichier pivotFun.sql pour créer une nouvelle fonction 3) Utilisez la fonction en SQL normal.

Soyez prudent avec les noms de colonnes dynamiques. Dans mon environnement, le nom de colonne est limité à 30 caractères et ne peut contenir qu'un seul guillemet. Donc, ma requête ressemble maintenant à ceci:

SELECT 
  *
FROM   
  table( 
        pivot('
                SELECT DISTINCT
                    P.proj_id,
                    REPLACE(substr(T.UDF_TYPE_LABEL, 1, 30), '''''''','','') as Attribute,
                    CASE
                      WHEN V.udf_text is null     and V.udf_date is null and      V.udf_number is NOT null  THEN to_char(V.udf_number)
                      WHEN V.udf_text is null     and V.udf_date is NOT null and  V.udf_number is null      THEN to_char(V.udf_date)
                      WHEN V.udf_text is NOT null and V.udf_date is null and      V.udf_number is null      THEN V.udf_text
                      ELSE NULL END
                    AS VALUE
                FROM
                    project   P
                LEFT JOIN UDFVALUE V ON P.proj_id     = V.proj_id 
                LEFT JOIN UDFTYPE  T ON V.UDF_TYPE_ID = T.UDF_TYPE_ID
                WHERE 
                    P.delete_session_id  IS NULL AND
                    T.TABLE_NAME = ''PROJECT''
    ')
)

Fonctionne bien avec des enregistrements allant jusqu'à 1 m. 

1
user7144213

Je ne vais pas répondre exactement à la question posée par OP, je vais simplement décrire comment le pivot dynamique peut être réalisé.

Ici, nous devons utiliser SQL dynamique, en récupérant d'abord les valeurs de colonne dans une variable et en passant la variable à l'intérieur de SQL dynamique.

EXEMPLE

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

Si nécessaire, vous pouvez également créer une table temporaire et effectuer une requête de sélection dans cette table temporaire pour afficher les résultats. C'est simple, il suffit d'ajouter le CREATE TABLE TABLENAME AS dans le code ci-dessus.

sqlqry :=
'    
  CREATE TABLE TABLENAME AS
  select * from
0
Sarath Avanavu