Chvíli trvalo, než jsem odpověděl, ale musel jsem to všechno napsat a vyzkoušet!
Data, se kterými jsem pracoval:
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 v současné podobě neumožňuje jednoduchým způsobem dynamický počet sloupců. Umožňuje to pouze s klíčovým slovem XML, což vede ke sloupci xmltype. Zde jsou některé vynikající dokumenty. http://www.oracle-base .com/articles/11g/pivot-and-unpivot-operators-11gr1.php
Vždy se vyplatí přečíst si je jako první.
Jak tedy na to?
Jakmile začnete hledat, najdete doslova tuny otázek na stejnou věc.
Dynamické SQL
- https:/ /asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:4471013000346257238
- Dynamické pivotování tabulky Oracle
- Dynamic Oracle Pivot_In_Clause
Klasická sestava může mít tělo funkce vracející příkaz sql jako návrat. Interaktivní zpráva nemůže. Jak to stojí, IR nepřichází v úvahu, protože je příliš závislé na metadatech.
Například s těmito dotazy/plsql v klasickém zdroji oblasti sestavy:
statický pivot
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
příkaz vracející tělo funkce
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;
Poznamenejte si však nastavení ve zdroji regionu.
- Používejte názvy sloupců specifické pro dotaz a ověřte dotaz
Toto je standardní nastavení. Zanalyzuje váš dotaz a poté uloží sloupce nalezené v dotazu do metadat sestavy. Pokud budete pokračovat a vytvoříte sestavu s výše uvedeným kódem plsql, můžete vidět, že apex analyzoval dotaz a přiřadil správné sloupce. Co je na tomto přístupu špatné, je to, že metadata jsou statická. Metadata přehledu se neobnovují při každém spuštění přehledu.
To lze snadno prokázat přidáním další třídy k datům.
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;
Spusťte stránku bez úprav sestavy! Úpravou a uložením dojde k regeneraci metadat, což zjevně není životaschopná metoda. Data se stejně změní a nemůžete pokaždé přejít a uložit metadata sestavy.
--cleanup
begin
delete from class where id = 3;
delete from class_meeting where id = 5;
delete from meeting_attendance where id = 10;
end;
- Používejte obecné názvy sloupců (analyzovat dotaz pouze za běhu)
Nastavení zdroje na tento typ vám umožní použít dynamičtější přístup. Změnou nastavení sestavy na tento typ analýzy vygeneruje apex pouze určitý počet sloupců ve svých metadatech, aniž by byl přímo spojen se skutečným dotazem. Budou tam pouze sloupce s 'COL1', 'COL2', 'COL3',...
Spusťte přehled. Funguje dobře. Nyní vložte některá data znovu.
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;
Spusťte přehled. Funguje dobře.
Zlomem jsou však názvy sloupců. S jejich ošklivými jmény opravdu nejsou tak dynamičtí. Sloupce můžete samozřejmě upravit, ale nejsou dynamické. Nezobrazuje se žádná třída nebo tak něco, ani nemůžete spolehlivě nastavit jejich záhlaví na jednu. Opět to dává smysl:metadata tam jsou, ale jsou statická. Mohlo by to pro vás fungovat, pokud jste s tímto přístupem spokojeni.
S tím se však můžete vypořádat. V části "Atributy sestavy" sestavy můžete vybrat "Typ nadpisů". Všechny jsou statické, očekávejte samozřejmě "PL/SQL"! Zde můžete napsat tělo funkce (nebo jen zavolat funkci), která vrátí záhlaví sloupců!
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;
Řešení třetí strany
- ://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/
V APEX: i když je dynamický pivot po instalaci přímočařejší, nastavení v apexu zůstává stejné, jako kdybyste chtěli používat dynamické SQL. Použijte klasickou sestavu s obecnými názvy sloupců.
Nebudu zde zacházet do velkých podrobností. Tento balíček nemám nainstalovaný atm. Je hezké mít, ale v tomto scénáři to nemusí být tak užitečné. To vám čistě umožňuje napsat dynamický pivot stručnějším způsobem, ale na vrcholové stránce věci moc nepomáhá. Jak jsem ukázal výše, omezujícím faktorem jsou zde dynamické sloupce a statická metadata vrcholových sestav.
Použít XML
Sám jsem se dříve rozhodl použít klíčové slovo XML. Používám pivot, abych se ujistil, že mám hodnoty pro všechny řádky a sloupce, a poté je znovu přečtu pomocí XMLTABLE
a poté vytvoření jednoho XMLTYPE
sloupec, serializovat jej do CLOB
.
Toto je možná trochu pokročilé, ale je to technika, kterou jsem zatím párkrát použil, s dobrými výsledky. Je to rychlé, za předpokladu, že základní data nejsou příliš velká a je to jen jedno volání SQL, takže není mnoho přepínání kontextu. Použil jsem to také s daty CUBE'd a funguje to skvěle.
(poznámka:třídy, které jsem přidal do prvků, odpovídají třídám používaným v klasických sestavách v tématu 1, jednoduché červené)
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;
V APEX: dobře, protože HTML bylo vytvořeno, může to být pouze oblast PLSQL, která volá funkci balíčku a vytiskne ji pomocí HTP.PRN
.
(edit) Na fóru OTN je také tento příspěvek, který z velké části dělá totéž, ale negeneruje nadpisy atd., ale používá funkce apex:OTN:Matrix report
PLSQL
Alternativně se můžete rozhodnout jít po dobré cestě plsql. Tělo můžete převzít z dynamického sql výše, zacyklit se a vytvořit strukturu tabulky pomocí htp.prn
hovory. Vyndejte hlavičky a dejte si cokoli jiného chcete. Pro dobrý efekt přidejte třídy k prvkům, které odpovídají motivu, který používáte.