Question

J'essaie de générer un rapport à partir de deux tables de base de données. La version simplifiée ressemble à ceci

Campaign
----------
CampaignID

Source
-----------------------
Source_ID | Campaign_ID

Content
---------------------------------------------------------
Content_ID | Campaign_ID | Content_Row_ID | Content_Value

Le rapport doit se lire comme suit:

CampaignID - SourceID - ContentRowID(Value(A)) - ContentRowID(Value(B))

Où ContentRowID (Valeur (A)) signifie "Rechercher une ligne" a un ID de campagne donné et un ContentRowId de "A". puis obtenez la valeur ContentValue pour cette ligne "

En gros, je dois "pivoter". (Je pense que c'est le bon terme) les lignes en colonnes ...

C'est une base de données Oracle 10g ...

Des suggestions?

Était-ce utile?

La solution

C’est ma première tentative. Le raffinement viendra une fois que j'en saurai plus sur le contenu du tableau de contenu.

Tout d'abord, vous avez besoin d'une table temporaire:

CREATE TABLE pivot (count integer);
INSERT INTO pivot VALUES (1);
INSERT INTO pivot VALUES (2);

Nous sommes maintenant prêts à interroger.

SELECT campaignid, sourceid, a.contentvalue, b.contentvalue
FROM content a, content b, pivot, source
WHERE source.campaignid = content.campaignid
AND pivot = 1 AND a.contentrowid = 'A'
AND pivot = 2 AND b.contentrowid = 'B'

Autres conseils

Bill Karwin le mentionne, mais je pense que cela mérite d’être souligné très clairement:

SQL ne fait pas ce que vous demandez, donc aucune "solution" vous obtenez va être un kludge.

Si vous savez , il fonctionnera toujours sur un Oracle 10, alors bien sûr, la tabulation croisée de Walter Mitty pourrait le faire. La bonne façon de le faire est de travailler avec la combinaison la plus simple d’ordre de tri dans la requête et le code de l’application pour bien le présenter.

  • Cela fonctionne sur d'autres systèmes de base de données,
  • cela ne risque pas de faire craquer d'autres couches (je me souviens d'avoir un problème avec> 255 colonnes par exemple. Êtes-vous sûr que vous la bibliothèque d'interface se débrouille aussi bien que la base de données elle-même?)
  • ce n'est (généralement) pas beaucoup plus difficile.

Si vous en avez besoin, vous pouvez simplement demander d’abord les ID_Row_ID , puis les lignes dont vous avez besoin, classées par CampaignID , ContentRowID , qui vous donnerait chaque cellule (remplie) dans un ordre ligne à ligne de gauche à droite.

Ps.

Il existe de nombreuses choses que l'homme moderne pense que SQL devrait avoir / faire et qui ne sont tout simplement pas là. En voici une, les plages générées en sont une autre, fermeture récursive, paramétrique ORDER BY , langage de programmation normalisé ... la liste est longue. (bien que, certes, il existe un truc pour ORDER BY )

Si vous n'avez pas un nombre dynamique de colonnes et que votre jeu de données n'est pas trop volumineux, vous pouvez le faire ...

SELECT CampaignID, SourceID, 
   (SELECT Content_Value FROM Content c 
      WHERE c.Campaign_ID=s.Campaign_ID 
      AND Content_Row_ID = 39100 
      AND rownum<=1) AS Value39100,
   (SELECT Content_Value FROM Content c 
      WHERE c.Campaign_ID=s.Campaign_ID 
      AND Content_Row_ID = 39200 
      AND rownum<=1) AS Value39200
FROM Source s;

Répétez la sous-requête pour chaque Content_Row_ID additionnel.

Pour ce faire en SQL standard, vous devez connaître toutes les valeurs distinctes de Content_Row_ID et effectuer une jointure par valeur distincte. Ensuite, vous avez besoin d'une colonne par valeur distincte de Content_Row_ID.

SELECT CA.Campaign_ID, 
  C1.Content_Value AS "39100",
  C2.Content_Value AS "39200",
  C3.Content_Value AS "39300"
FROM Campaign CA
  LEFT OUTER JOIN Content C1 ON (CA.Campaign_ID = C1.Campaign_ID 
    AND C1.Content_Row_ID = 39100)
  LEFT OUTER JOIN Content C2 ON (CA.Campaign_ID = C2.Campaign_ID 
    AND C2.Content_Row_ID = 39200)
  LEFT OUTER JOIN Content C3 ON (CA.Campaign_ID = C3.Campaign_ID 
    AND C3.Content_Row_ID = 39300);

À mesure que le nombre de valeurs distinctes augmente, cette requête devient trop coûteuse pour être exécutée efficacement. Il est probablement plus facile de récupérer les données plus simplement et de les reformater en PL / SQL ou en code d'application.

Bill Karwin et Anders Eurenius ont raison de dire qu’il n’existe aucune solution simple ni solution du tout lorsque le nombre de valeurs de colonne obtenues n’est pas connu à l’avance. Oracle 11g le simplifie quelque peu avec l'opérateur PIVOT , mais les colonnes doivent encore être connues à l'avance et ne répondent pas au critère 10g de votre question.

Si vous avez besoin d'un nombre dynamique de colonnes, je ne pense pas que cela puisse être fait en SQL standard, ce qui, hélas, dépasse mes connaissances. Mais certaines fonctionnalités d'Oracle peuvent le faire. J'ai trouvé des ressources:

http://www.sqlsnippets.com/fr/topic-12200.html

http: //asktom.oracle.com/pls/asktom/f?p=100:11::::::::11:QUES:ID12121212808063#41097616566309

Si vous avez " Oracle, la référence complète " recherchez une section intitulée "Tourner une table sur le côté". Vous y trouverez des exemples détaillés et des instructions pour effectuer un pivot, bien que l’édition que j’ai n’appelle pas cela un pivot.

Autre terme pour "faire pivoter une table" est le tableau croisé.

MS Access est l’un des outils les plus simples à utiliser pour effectuer des analyses croisées. Si vous avez MS Access et que vous pouvez établir un lien de table à partir d'une base de données Access vers votre table source, vous êtes déjà à mi-chemin.

À ce stade, vous pouvez activer le "Assistant de requête" et lui demander de créer une requête d'analyse croisée pour vous. C'est aussi simple que de répondre aux questions de l'assistant. Le côté malheureux de cette solution est que, si vous regardez la requête résultante dans la vue SQL, vous verrez du code SQL spécifique au dialecte Access de SQL et qui ne peut pas être utilisé, en général, sur d’autres plateformes.

Vous pourrez également télécharger des outils d’analyse simples à partir du site Web d’Oracle et utiliser l’un de ces outils pour effectuer une analyse croisée à votre place.

Encore une fois, si vous voulez vraiment le faire en SQL, "Oracle, la référence complète" devrait vous aider.

Si vous ne connaissez pas le nombre de colonnes à l’avant, relancez une requête SQL normale et utilisez le code côté serveur comme indiqué ci-dessous: Remplissage de la grille de données et de la requête SQL

J'ai fait une solution avec ce SQL. J'avais besoin que les lignes soient le nombre de classes et les colonnes le résumé de chaque classe par mois, donc, la première colonne est le résumé de la ligne et chaque colonne est le résumé de chaque mois, et la dernière ligne est le résumé colonne complète mois par mois.

Bonne chance

Select DS.Cla,
Sum(case
when (Extract(year from DS.Data) =:intYear) then DS.PRE
else 0
end) as ToTal,
Sum(case
when (Extract(month from DS.Data) =1) then DS.PRE
else 0
end) as Jan,
Sum(case
when (Extract(month from DS.Data) =2) then DS.PRE
else 0
end) as FEV,
Sum(case
when (Extract(month from DS.Data) =3) then DS.PRE
else 0
end) as MAR,
Sum(case
when (Extract(month from DS.Data) =4) then DS.PRE
else 0
end) as ABR,
Sum(case
when (Extract(month from DS.Data) =5) then DS.PRE
else 0
end) as MAI,
Sum(case
when (Extract(month from DS.Data) =6) then DS.PRE
else 0
end) as JUN,
Sum(case
when (Extract(month from DS.Data) =7) then DS.PRE
else 0
end) as JUL,
Sum(case
when (Extract(month from DS.Data) =8) then DS.PRE
else 0
end) as AGO,
Sum(case
when (Extract(month from DS.Data) =9) then DS.PRE
else 0
end) as SETE,
Sum(case
when (Extract(month from DS.Data) =10) then DS.PRE
else 0
end) as OUT,
Sum(case
when (Extract(month from DS.Data) =11) then DS.PRE
else 0
end) as NOV,
Sum(case
when (Extract(month from DS.Data) =12) then DS.PRE
else 0
end) as DEZ
from Dados DS
Where DS.Cla > 0
And Extract(Year from DS.Data) = :intYear
group by DS.CLA

Union All

Select 0*count(DS.cla),  0*count(DS.cla),
Sum(case
when (Extract(month from DS.Data) =1) then DS.PRE
else 0
end) as JAN,
Sum(case
when (Extract(month from DS.Data) =2) then DS.PRE
else 0
end) as FEV,
Sum(case
when (Extract(month from DS.Data) =3) then DS.PRE
else 0
end) as MAR,
Sum(case
when (Extract(month from DS.Data) =4) then DS.PRE
else 0
end) as ABR,
Sum(case
when (Extract(month from DS.Data) =5) then DS.PRE
else 0
end) as MAI,
Sum(case
when (Extract(month from DS.Data) =6) then DS.PRE
else 0
end) as JUN,
Sum(case
when (Extract(month from DS.Data) =7) then DS.PRE
else 0
end) as JUL,
Sum(case
when (Extract(month from DS.Data) =8) then DS.PRE
else 0
end) as AGO,
Sum(case
when (Extract(month from DS.Data) =9) then DS.PRE
else 0
end) as SETE,
Sum(case
when (Extract(month from DS.Data) =10) then DS.PRE
else 0
end) as OUT,
Sum(case
when (Extract(month from DS.Data) =11) then DS.PRE
else 0
end) as NOV,
Sum(case
when (Extract(month from DS.Data) =12) then DS.PRE
else 0
end) as DEZ
from Dados DS
Where DS.Cla > 0
And Extract(Year from DS.Data) = :intYear
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top