“ピボット” SQLのテーブル(クロス集計/クロス集計)
質問
いくつかのデータベーステーブルからレポートを生成しようとしています。簡易版は次のようになります
Campaign
----------
CampaignID
Source
-----------------------
Source_ID | Campaign_ID
Content
---------------------------------------------------------
Content_ID | Campaign_ID | Content_Row_ID | Content_Value
レポートは次のように読む必要があります:
CampaignID - SourceID - ContentRowID(Value(A)) - ContentRowID(Value(B))
ContentRowID(Value(A))は、「特定のCampaignIDと「A」のContentRowIdを持つ行を検索する」という意味です。その後、その行のContentValueを取得します
本質的に、「ピボット」する必要があります; (正しい用語だと思う)行を列に...
これはOracle 10gデータベースです...
提案はありますか
解決
これは私の最初の突き刺しです。コンテンツテーブルの内容について詳しく知ったら、改良を加えます。
最初に、一時テーブルが必要です:
CREATE TABLE pivot (count integer);
INSERT INTO pivot VALUES (1);
INSERT INTO pivot VALUES (2);
これでクエリの準備ができました。
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'
他のヒント
ビル・カーウィンはこれについて言及していますが、これは非常に明確に指摘するに値すると思います:
SQLはユーザーが求めていることを実行しません。そのため、" solution"あなたが得るのは手品になるだろう。
知っている場合、確かに、常にOracle 10で実行されます。そして、Walter Mittyのクロス集計がそれを行うかもしれません。正しい方法は、クエリとアプリケーションコードで並べ替え順序の最も簡単な組み合わせを使用して、正しくレイアウトすることです。
- 他のデータベースシステムで動作します
- 他のレイヤーが壊れるリスクはありません(たとえば、MySQLで> 255カラムの問題があることを覚えています。データベース自体と同様にインターフェイスライブラリに対応していますか?)
- (通常)それほど難しくありません。
必要な場合は、最初に Content_Row_ID
を要求してから、必要な行を CampaignID
、 ContentRowID
、左から右、行ごとの順序で各(入力された)セルを提供します。
Ps。
SQLが持っているべき/やるべきだと現代人が考えるものはたくさんありますが、それはそこにはありません。これは1つ、生成された範囲はもう1つ、再帰的クロージャー、パラメトリックな ORDER BY
、標準化されたプログラミング言語...リストが続きます。 (ただし、確かに、 ORDER BY
にはトリックがあります)
動的な列数がなく、データセットが大きすぎない場合、これを行うことができます...
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;
追加のContent_Row_IDごとにサブクエリを繰り返します。
標準SQLでこれを行うには、Content_Row_IDの個別の値をすべて把握し、個別の値ごとに結合する必要があります。次に、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);
個別の値の数が増えると、このクエリはコストがかかりすぎて効率的に実行できなくなります。データをより簡単にフェッチし、PL / SQLまたはアプリケーションコードで再フォーマットする方がおそらく簡単です。
Bill KarwinとAnders Eureniusは、簡単な解決策はなく、結果の列値の数が事前にわからない場合はまったく解決策がないことを認識しています。 Oracle 11gは、 PIVOT演算子、ただし列は事前に知っておく必要があり、質問の10g基準を満たしていません。
動的な数の列が必要な場合、これが標準のSQLで実行できるとは思いません。しかし、それを実行できるOracleの機能があります。リソースを見つけました:
http://www.sqlsnippets.com/en/topic-12200.html
http: //asktom.oracle.com/pls/asktom/f?p=100:11:0:::::P11_QUESTION_ID:124812348063#41097616566309
「Oracle、完全なリファレンス」がある場合「表を回転させる」というタイトルのセクションを探してください。これは、ピボットを実行するための詳細な例と手順を示していますが、私が持っているエディションではピボットとは呼びません。
「テーブルのピボット」の別の用語クロス集計です。
クロス集計の実行に使用する最も簡単なツールの1つはMS Accessです。 MS Accessを使用していて、Accessデータベースからソーステーブルへのテーブルリンクを確立できる場合は、すでに途中です。
その時点で、「クエリウィザード」を起動し、クロス集計クエリを作成するよう依頼できます。ウィザードからの質問に答えるのと同じくらい簡単です。このソリューションの残念な点は、結果のクエリをSQLビューで見ると、SQLのAccessダイアレクトに特有のSQLが表示され、一般に他のプラットフォームで使用できないことです。
Oracle Webサイトからいくつかの簡単な分析ツールをダウンロードし、それらのツールのいずれかを使用してクロス集計を実行することもできます。
もう一度、SQLで本当にやりたい場合は、「Oracle、完全なリファレンス」、あなたを助けるはずです。
前の列の数がわからない場合は、通常のSQLクエリを戻し、ここにリストしたようなサーバー側コードを使用します:データグリッドとSQLクエリの入力
IこのSQLでソリューションを作成しました。行はクラスの数であり、列は月ごとの各クラスの合計である必要があるため、最初の列は行の合計であり、各列は各月の合計であり、最後の行は合計です月ごとの完全な列の例。
幸運
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