Het duurde even voordat ik antwoord kreeg, maar ik moest dit allemaal opschrijven en testen!
Gegevens waarmee ik heb gewerkt:
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 staat, zoals het er nu voor staat, niet op een simpele manier een dynamisch aantal kolommen toe. Het staat dit alleen toe met het XML-sleutelwoord, wat resulteert in een xmltype-kolom. Hier zijn enkele uitstekende documenten. http://www.oracle-base .com/articles/11g/pivot-and-unpivot-operators-11gr1.php
Het loont altijd om die eerst te lezen.
Hoe dan?
Je zult letterlijk talloze vragen over hetzelfde vinden als je eenmaal begint te zoeken.
Dynamische SQL
- https:/ /asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:4471013000346257238
- Dynamisch draaien van een Oracle-tabel
- Dynamisch Oracle Pivot_In_Clause
Een klassiek rapport kan een functietekst hebben die een sql-instructie retourneert. Een interactief rapport kan dat niet. Zoals het er nu uitziet, is een IR uitgesloten omdat het te afhankelijk is van metadata.
Bijvoorbeeld met deze query's/plsql in een klassieke bron voor rapportregio's:
statische spil
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
function body return statement
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;
Let echter op de instellingen in de regiobron.
- Gebruik queryspecifieke kolomnamen en valideer query
Dit is de standaard instelling. Uw query wordt geparseerd en vervolgens worden de kolommen in de query opgeslagen in de metagegevens van het rapport. Als u doorgaat en een rapport maakt met de bovenstaande plsql-code, kunt u zien dat apex de query heeft geparseerd en de juiste kolommen heeft toegewezen. Wat er mis is met deze aanpak is dat die metadata statisch is. De metagegevens van het rapport worden niet telkens vernieuwd wanneer het rapport wordt uitgevoerd.
Dit kan eenvoudig worden bewezen door een andere klasse aan de gegevens toe te voegen.
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;
Voer de pagina uit zonder het rapport te bewerken! Bewerken en opslaan zal de metadata opnieuw genereren, wat duidelijk geen haalbare methode is. De gegevens zullen hoe dan ook veranderen en u kunt niet elke keer naar binnen gaan en de metagegevens van het rapport opslaan.
--cleanup
begin
delete from class where id = 3;
delete from class_meeting where id = 5;
delete from meeting_attendance where id = 10;
end;
- Gebruik generieke kolomnamen (query alleen tijdens runtime ontleden)
Als u de bron op dit type instelt, kunt u een meer dynamische benadering gebruiken. Door de instellingen van het rapport te wijzigen in dit type parsing, genereert apex slechts een aantal kolommen in zijn metadata zonder direct te worden geassocieerd met de eigenlijke query. Er zullen alleen kolommen zijn met 'COL1', 'COL2', 'COL3',...
Voer het rapport uit. Werkt prima. Voer nu opnieuw wat gegevens in.
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;
Voer het rapport uit. Werkt prima.
De knik hier zijn echter de kolomnamen. Ze zijn niet echt zo dynamisch, met hun lelijke namen. U kunt de kolommen zeker bewerken, maar ze zijn niet dynamisch. Er wordt geen klasse weergegeven of zoiets, en je kunt hun headers ook niet betrouwbaar op één instellen. Nogmaals, dit is logisch:de metadata is er, maar het is statisch. Het kan voor u werken als u tevreden bent met deze aanpak.
U kunt dit echter wel aan. In de "Rapportattributen" van het rapport kunt u een "Type koptekst" selecteren. Ze zijn allemaal statisch, verwacht natuurlijk "PL/SQL"! Hier kun je een functietekst schrijven (of gewoon een functie aanroepen) die de kolomkoppen teruggeeft!
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;
Oplossing van derden
- 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/
In APEX: hoewel de dynamische spil na installatie eenvoudiger is, blijft de installatie in apex hetzelfde alsof u dynamische SQL zou willen gebruiken. Gebruik een klassiek rapport met algemene kolomnamen.
Ik ga hier niet veel in detail treden. Ik heb dit pakket niet geïnstalleerd atm. Het is leuk om te hebben, maar in dit scenario is het misschien niet zo handig. Het stelt je puur in staat om een dynamische spil op een meer beknopte manier te schrijven, maar helpt niet veel aan de topkant van de dingen. Zoals ik hierboven heb aangetoond, zijn de dynamische kolommen en de statische metadata van de apex-rapporten hier de beperkende factor.
XML gebruiken
Zelf heb ik er al eerder voor gekozen om het XML-trefwoord te gebruiken. Ik gebruik pivot om ervoor te zorgen dat ik waarden heb voor alle rijen en kolommen, en lees het dan opnieuw voor met XMLTABLE
en maak vervolgens een XMLTYPE
kolom, serialiseren naar een CLOB
.
Dit is misschien een beetje geavanceerd, maar het is een techniek die ik tot nu toe een paar keer heb gebruikt, met goede resultaten. Het is snel, op voorwaarde dat de basisgegevens niet te groot zijn, en het is slechts één sql-aanroep, dus niet veel contextwisselingen. Ik heb het ook met CUBE-gegevens gebruikt en het werkt geweldig.
(let op:de klassen die ik aan de elementen heb toegevoegd, komen overeen met klassen die worden gebruikt in klassieke rapporten in thema 1, simpel rood)
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;
In APEX: Nou, aangezien de HTML is geconstrueerd, kan dit alleen een PLSQL-regio zijn die de pakketfunctie aanroept en deze afdrukt met HTP.PRN
.
(edit) Er is ook dit bericht op het OTN-forum dat grotendeels hetzelfde doet, maar geen koppen enz. genereert, maar eerder de apex-functionaliteiten gebruikt:OTN:Matrixrapport
PLSQL
Je kunt er ook voor kiezen om de goede oude plsql-route te volgen. Je zou de body van de dynamische sql hierboven kunnen nemen, eroverheen kunnen lussen en een tabelstructuur kunnen maken met behulp van htp.prn
belt. Zet headers op, en zet uit wat je nog meer wilt. Voeg voor een goed effect klassen toe aan de elementen die overeenkomen met het thema dat u gebruikt.