Det tog et stykke tid at svare, men jeg var nødt til at skrive det hele op og teste det!
Data jeg har arbejdet med:
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 , som det står lige nu, tillader ikke et dynamisk antal kolonner på en enkel måde. Det tillader kun dette med XML-nøgleordet, hvilket resulterer i en xmltype-kolonne. Her er nogle fremragende dokumenter. http://www.oracle-base .com/articles/11g/pivot-and-unpivot-operators-11gr1.php
Det kan altid betale sig at læse dem først.
Hvordan gør man så?
Du vil bogstaveligt talt finde tonsvis af spørgsmål om det samme, når du begynder at søge.
Dynamisk SQL
- https:/ /asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:4471013000346257238
- Dynamisk drejning af et bord Oracle
- Dynamisk Oracle Pivot_In_Clause
En klassisk rapport kan tage en funktionstekst, der returnerer en sql-sætning, som retur. En interaktiv rapport kan ikke. Som det står, er en IR udelukket, da den er for metadataafhængig.
For eksempel med disse forespørgsler/plsql i en klassisk rapportområdekilde:
statisk 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
funktionstekst returnerende erklæring
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;
Vær dog opmærksom på indstillingerne i regionskilden.
- Brug forespørgselsspecifikke kolonnenavne og valider forespørgsel
Dette er standardindstillingen. Det vil analysere din forespørgsel og derefter gemme kolonnerne i forespørgslen i rapportens metadata. Hvis du går videre og opretter en rapport med ovenstående plsql-kode, kan du se, at apex har parset forespørgslen og har tildelt de korrekte kolonner. Hvad der er galt med denne tilgang er, at de metadata er statiske. Rapportens metadata opdateres ikke, hver gang rapporten køres.
Dette kan bevises ganske enkelt ved at tilføje en anden klasse til dataene.
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;
Kør siden uden at redigere rapporten! Redigering og lagring vil regenerere metadataene, hvilket tydeligvis ikke er en holdbar metode. Dataene ændres alligevel, og du kan ikke gå ind og gemme rapportens metadata hver gang.
--cleanup
begin
delete from class where id = 3;
delete from class_meeting where id = 5;
delete from meeting_attendance where id = 10;
end;
- Brug generiske kolonnenavne (kun parse forespørgsel ved kørsel)
Indstilling af kilden til denne type vil give dig mulighed for at bruge en mere dynamisk tilgang. Ved at ændre indstillingerne for rapporten til denne type parsing, vil apex blot generere en mængde kolonner i dens metadata uden at være direkte forbundet med den faktiske forespørgsel. Der vil kun være kolonner med 'COL1', 'COL2', 'COL3',...
Kør rapporten. Fungerer fint. Indsæt nu nogle data igen.
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;
Kør rapporten. Fungerer fint.
Knikken her er dog kolonnenavnene. De er egentlig ikke så dynamiske med deres grimme navne. Du kan selvfølgelig redigere kolonnerne, men de er ikke dynamiske. Der er ingen klasse, der vises eller noget, og du kan heller ikke pålideligt indstille deres overskrifter til en. Igen giver det mening:Metadataene er der, men de er statiske. Det kunne fungere for dig, hvis du er tilfreds med denne tilgang.
Du kan dog håndtere dette. I rapportens "Rapportattributter" kan du vælge en "Overskriftstype". De er alle statiske, forvent selvfølgelig "PL/SQL"! Her kan du skrive en funktionstekst (eller bare kalde en funktion), som returnerer kolonneoverskrifterne!
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;
Tredjepartsløsning
- https3 ://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/
I APEX: selvom den dynamiske pivot er mere ligetil efter installation, forbliver opsætningen i apex den samme, som hvis du ville bruge dynamisk SQL. Brug en klassisk rapport med generiske kolonnenavne.
Jeg vil ikke gå i detaljer her. Jeg har ikke denne pakke installeret atm. Det er rart at have, men i dette scenarie er det måske ikke så nyttigt. Det giver dig udelukkende mulighed for at skrive et dynamisk pivot på en mere kortfattet måde, men hjælper ikke meget på spidssiden af tingene. Som jeg har demonstreret ovenfor, er de dynamiske kolonner og de statiske metadata i apex-rapporterne den begrænsende faktor her.
Brug XML
Jeg har selv valgt at bruge XML søgeordet før. Jeg bruger pivot for at sikre, at jeg har værdier for alle rækker og kolonner, og læs det derefter op igen med XMLTABLE
, og derefter oprette en XMLTYPE
kolonne, serialiserer den til en CLOB
.
Dette er måske lidt avanceret, men det er en teknik, jeg har brugt et par gange indtil videre, med gode resultater. Det er hurtigt, forudsat at basisdataene ikke er for store, og det kun er et sql-kald, så der er ikke mange kontekstskift. Jeg har også brugt det med CUBE'd-data, og det fungerer godt.
(bemærk:de klasser, jeg har tilføjet på elementerne, svarer til klasser, der bruges på klassiske rapporter i tema 1, simpel rød)
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;
I APEX: godt, da HTML'en er blevet konstrueret, kan dette kun være en PLSQL-region, som kalder pakkefunktionen og udskriver den ved hjælp af HTP.PRN
.
(rediger) Der er også dette indlæg på OTN-forummet, som gør det samme for en stor del, men ikke genererer overskrifter osv., snarere ved at bruge apex-funktionaliteterne:OTN:Matrix-rapport
PLSQL
Alternativt kan du bare vælge at gå den gode gamle plsql-rute. Du kan tage brødteksten fra den dynamiske sql ovenfor, sløjfe over den og udsende en tabelstruktur ved at bruge htp.prn
opkald. Sæt overskrifter ud, og læg hvad du ellers vil have. For god effekt, tilføje klasser på de elementer, der svarer til det tema, du bruger.