答えるのに少し時間がかかりましたが、これをすべて書き留めてテストする必要がありました!
私が扱ったデータ:
begin
insert into student(id, name) values (1, 'Tom');
insert into student(id, name) values (2, 'Odysseas');
insert into class(id, subject) values (1, 'Programming');
insert into class(id, subject) values (2, 'Databases');
insert into class_meeting (id, class_id, meeting_sequence) values (1, 1, 10);
insert into class_meeting (id, class_id, meeting_sequence) values (2, 1, 20);
insert into class_meeting (id, class_id, meeting_sequence) values (3, 2, 10);
insert into class_meeting (id, class_id, meeting_sequence) values (4, 2, 20);
insert into meeting_attendance (id, student_id, meeting_id, present) values (1, 1, 1, 1); -- Tom was at meeting 10 about programming
insert into meeting_attendance (id, student_id, meeting_id, present) values (2, 1, 2, 1); -- Tom was at meeting 20 about programming
insert into meeting_attendance (id, student_id, meeting_id, present) values (3, 1, 3, 0); -- Tom was NOT at meeting 10 about databases
insert into meeting_attendance (id, student_id, meeting_id, present) values (4, 1, 4, 0); -- Tom was NOT at meeting 20 about databases
insert into meeting_attendance (id, student_id, meeting_id, present) values (5, 2, 1, 0); -- Odysseas was NOT at meeting 10 about programming
insert into meeting_attendance (id, student_id, meeting_id, present) values (6, 2, 2, 1); -- Odysseas was at meeting 20 about programming
insert into meeting_attendance (id, student_id, meeting_id, present) values (7, 2, 3, 0); -- Odysseas was NOT at meeting 10 about databases
insert into meeting_attendance (id, student_id, meeting_id, present) values (8, 2, 4, 1); -- Odysseas was at meeting 20 about databases
end;
PIVOTは、現在のところ、単純な方法で動的な列数を許可していません。これはXMLキーワードでのみ許可され、xmltype列になります。ここにいくつかの優れたドキュメントがあります。 http://www.oracle-base .com / articles / 11g / plugin-and-unpivot-operators-11gr1.php
最初にそれらを読むことは常に報われます。
それでは、どうすればよいですか?
検索を開始すると、同じことについて文字通りたくさんの質問が見つかります。
動的SQL
- https:/ /asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:4471013000346257238
- テーブルOracleを動的にピボットする>
- 動的OraclePivot_In_Clause
従来のレポートでは、SQLステートメントを返す関数本体をreturnとして受け取ることができます。インタラクティブレポートはできません。現状では、IRはメタデータに依存しすぎているため、問題外です。
たとえば、従来のレポート領域ソースでこれらのquerys / plsqlを使用すると、次のようになります。
静的ピボット
select *
from (
select s.name as student_name, m.present present, cm.meeting_sequence||'-'|| c.subject meeting
from student s
join meeting_attendance m
on s.id = m.student_id
join class_meeting cm
on cm.id = m.meeting_id
join class c
on c.id = cm.class_id
)
pivot ( max(present) for meeting in ('10-Databases' as "10-DB", '20-Databases' as "20-DB", '10-Programming' as "10-PRM", '20-Programming' as "20-PRM") );
-- Results
STUDENT_NAME '10-Databases' 20-DB 10-PRM 20-PRM
Tom 0 0 1 1
Odysseas 0 1 0 1
関数本体がステートメントを返す
DECLARE
l_pivot_cols VARCHAR2(4000);
l_pivot_qry VARCHAR2(4000);
BEGIN
SELECT ''''||listagg(cm.meeting_sequence||'-'||c.subject, ''',''') within group(order by 1)||''''
INTO l_pivot_cols
FROM class_meeting cm
JOIN "CLASS" c
ON c.id = cm.class_id;
l_pivot_qry :=
'select * from ( '
|| 'select s.name as student_name, m.present present, cm.meeting_sequence||''-''||c.subject meeting '
|| 'from student s '
|| 'join meeting_attendance m '
|| 'on s.id = m.student_id '
|| 'join class_meeting cm '
|| 'on cm.id = m.meeting_id '
|| 'join class c '
|| 'on c.id = cm.class_id '
|| ') '
|| 'pivot ( max(present) for meeting in ('||l_pivot_cols||') )' ;
RETURN l_pivot_qry;
END;
ただし、リージョンソースの設定に注意してください。
- クエリ固有の列名を使用してクエリを検証する
これが標準設定です。クエリを解析し、クエリで見つかった列をレポートメタデータに保存します。上記のplsqlコードを使用してレポートを作成すると、apexがクエリを解析し、正しい列を割り当てていることがわかります。このアプローチの何が問題なのかは、そのメタデータが静的であるということです。レポートのメタデータは、レポートが実行されるたびに更新されるわけではありません。
これは、データに別のクラスを追加するだけで非常に簡単に証明できます。
begin
insert into class(id, subject) values (3, 'Watch YouTube');
insert into class_meeting (id, class_id, meeting_sequence) values (5, 3, 10);
insert into meeting_attendance (id, student_id, meeting_id, present) values (10, 1, 5, 1); -- Tom was at meeting 10 about watching youtube
end;
レポートを編集せずにページを実行してください!編集して保存するとメタデータが再生成されますが、これは明らかに実行可能な方法ではありません。とにかくデータは変更されるため、毎回レポートのメタデータにアクセスして保存することはできません。
--cleanup
begin
delete from class where id = 3;
delete from class_meeting where id = 5;
delete from meeting_attendance where id = 10;
end;
- 一般的な列名を使用する(実行時にのみクエリを解析する)
ソースをこのタイプに設定すると、より動的なアプローチを使用できるようになります。レポートの設定をこのタイプの解析に変更することにより、apexは、実際のクエリに直接関連付けられることなく、メタデータに一定量の列を生成するだけです。 'COL1'、'COL2'、'COL3'、...
レポートを実行する列があります。正常に動作します。ここで、データをもう一度挿入します。
begin
insert into class(id, subject) values (3, 'Watch YouTube');
insert into class_meeting (id, class_id, meeting_sequence) values (5, 3, 10);
insert into meeting_attendance (id, student_id, meeting_id, present) values (10, 1, 5, 1); -- Tom was at meeting 10 about watching youtube
end;
レポートを実行します。正常に動作します。
ただし、ここでのねじれは列名です。醜い名前で、それほどダイナミックではありません。確かに列を編集することはできますが、動的ではありません。表示されているクラスなどはありません。また、ヘッダーを確実に1に設定することもできません。繰り返しますが、これは理にかなっています。メタデータはありますが、静的です。このアプローチに満足している場合は、うまくいく可能性があります。
ただし、これに対処することはできます。レポートの「レポート属性」で、「見出しタイプ」を選択できます。これらはすべて静的です。もちろん「PL/SQL」を期待してください。ここでは、列ヘッダーを返す関数本体を作成できます(または関数を呼び出すだけです)。
DECLARE
l_return VARCHAR2(400);
BEGIN
SELECT listagg(cm.meeting_sequence||'-'||c.subject, ':') within group(order by 1)
INTO l_return
FROM class_meeting cm
JOIN "CLASS" c
ON c.id = cm.class_id;
RETURN l_return;
END;
サードパーティソリューション
- https ://asktom.oracle.com/pls/apex/f?p =100:11:0 ::::P11_QUESTION_ID:4843682300346852395#5394721000346803830
- https://stackoverflow.com/a/16702401/814048
- http:// technology .amis.nl / 2006/05/24 / dynamic-sql-pivoting-stealing-antons-thunder /
APEXの場合: インストール後の動的ピボットはより簡単ですが、apexでのセットアップは、動的SQLを使用する場合と同じです。一般的な列名を使用した従来のレポートを使用します。
ここでは詳しく説明しません。このパッケージはATMにインストールされていません。持っているのはいいことですが、このシナリオではそれほど役に立たないかもしれません。それは純粋にあなたがより簡潔な方法で動的ピボットを書くことを可能にしますが、物事の頂点側ではあまり役に立ちません。上で示したように、ここでは、頂点レポートの動的列と静的メタデータが制限要因になります。
XMLを使用する
私自身、以前はXMLキーワードを使用することを選択しました。ピボットを使用してすべての行と列の値があることを確認してから、XMLTABLE
で再度読み取ります。 、次に1つのXMLTYPE
を作成します 列、CLOB
にシリアル化します 。
これは少し進んでいるかもしれませんが、これまでに2、3回使用した手法であり、良好な結果が得られています。基本データが大きすぎず、SQL呼び出しが1回だけなので、コンテキストスイッチがそれほど多くない場合は高速です。 CUBEのデータでも使用しましたが、うまく機能します。
(注:要素に追加したクラスは、テーマ1のクラシックレポートで使用されているクラスに対応しています)
DECLARE
l_return CLOB;
BEGIN
-- Subqueries:
-- SRC
-- source data query
-- SRC_PIVOT
-- pivoted source data with XML clause to allow variable columns.
-- Mainly used for convenience because pivot fills in 'gaps' in the data.
-- an example would be that 'Odysseas' does not have a relevant record for the 'Watch Youtube' class
-- PIVOT_HTML
-- Pulls the data from the pivot xml into columns again, and collates the data
-- together with xmlelments.
-- HTML_HEADERS
-- Creates a row with just header elements based on the source data
-- HTML_SRC
-- Creates row elements with the student name and the collated data from pivot_html
-- Finally:
-- serializes the xmltype column for easier-on-the-eye markup
WITH src AS (
SELECT s.name as student_name, m.present present, cm.meeting_sequence||'-'||c.subject meeting
FROM student s
JOIN meeting_attendance m
ON s.id = m.student_id
JOIN class_meeting cm
ON cm.id = m.meeting_id
JOIN class c
ON c.id = cm.class_id
),
src_pivot AS (
SELECT student_name, meeting_xml
FROM src pivot xml(MAX(NVL(present, 0)) AS is_present_max for (meeting) IN (SELECT distinct meeting FROM src) )
),
pivot_html AS (
SELECT student_name
, xmlagg(
xmlelement("td", xmlattributes('data' as "class"), is_present_max)
ORDER BY meeting
) is_present_html
FROM src_pivot
, xmltable('PivotSet/item'
passing meeting_xml
COLUMNS "MEETING" VARCHAR2(400) PATH 'column[@name="MEETING"]'
, "IS_PRESENT_MAX" NUMBER PATH 'column[@name="IS_PRESENT_MAX"]')
GROUP BY (student_name)
),
html_headers AS (
SELECT xmlelement("tr",
xmlelement("th", xmlattributes('header' as "class"), 'Student Name')
, xmlagg(xmlelement("th", xmlattributes('header' as "class"), meeting) order by meeting)
) headers
FROM (SELECT DISTINCT meeting FROM src)
),
html_src as (
SELECT
xmlagg(
xmlelement("tr",
xmlelement("td", xmlattributes('data' as "class"), student_name)
, ah.is_present_html
)
) data
FROM pivot_html ah
)
SELECT
xmlserialize( content
xmlelement("table"
, xmlattributes('report-standard' as "class", '0' as "cellpadding", '0' as "cellspacing", '0' as "border")
, xmlelement("thead", headers )
, xmlelement("tbody", data )
)
AS CLOB INDENT SIZE = 2
)
INTO l_return
FROM html_headers, html_src ;
htp.prn(l_return);
END;
APEXの場合: HTMLが作成されているので、これはパッケージ関数を呼び出してHTP.PRN
を使用して出力するPLSQL領域のみになります。 。
(編集)OTNフォーラムにもこの投稿があります。これは大部分が同じですが、見出しなどを生成せず、頂点機能を使用します: OTN:マトリックスレポート
PLSQL
または、古き良きplsqlルートを選択することもできます。上記の動的SQLから本体を取得し、それをループして、htp.prn
を使用してテーブル構造を出力できます。 呼び出します。ヘッダーを出し、他に好きなものを出します。効果を上げるには、使用しているテーマに対応する要素にクラスを追加します。