Oracle
 sql >> Teknologi Basis Data >  >> RDS >> Oracle

Apakah kueri berikut dimungkinkan dengan SQL Pivot?

Butuh beberapa saat untuk menjawab, tetapi saya harus menulis ini semua dan mengujinya!

Data yang saya gunakan:

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 , seperti yang ada sekarang, tidak mengizinkan jumlah kolom yang dinamis dengan cara yang sederhana. Ini hanya memungkinkan ini dengan kata kunci XML, menghasilkan kolom xmltype. Berikut adalah beberapa dokumen yang sangat baik. http://www.Oracle-base .com/articles/11g/pivot-and-unpivot-operators-11gr1.php
Membaca yang pertama selalu terbayar.

Lalu bagaimana caranya?
Anda akan benar-benar menemukan banyak pertanyaan tentang hal yang sama begitu Anda mulai mencari.

SQL Dinamis

Laporan klasik dapat mengambil badan fungsi yang mengembalikan pernyataan sql sebagai pengembalian. Laporan interaktif tidak bisa. Seperti berdiri, IR tidak mungkin karena terlalu bergantung pada metadata.

Misalnya, dengan kueri/plsql ini di sumber region laporan klasik:

poros statis

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

pernyataan pengembalian fungsi tubuh

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;

Namun perhatikan pengaturan di sumber wilayah.

  • Gunakan Nama Kolom Khusus Kueri dan Validasi Kueri

Ini adalah pengaturan standar. Ini akan mengurai kueri Anda dan kemudian menyimpan kolom yang ditemukan dalam kueri dalam metadata laporan. Jika Anda melanjutkan dan membuat laporan dengan kode plsql di atas, Anda dapat melihat bahwa apex telah menguraikan kueri dan telah menetapkan kolom yang benar. Apa yang salah dengan pendekatan ini adalah bahwa metadata itu statis. Metadata laporan tidak diperbarui setiap kali laporan dijalankan.
Hal ini dapat dibuktikan cukup sederhana dengan menambahkan kelas lain ke data.

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;

Jalankan halaman tanpa mengedit laporan! Mengedit dan menyimpan akan membuat ulang metadata, yang jelas bukan metode yang layak. Data akan tetap berubah, dan Anda tidak dapat masuk dan menyimpan metadata laporan setiap saat.

--cleanup
begin
delete from class where id = 3;
delete from class_meeting where id = 5;
delete from meeting_attendance where id = 10;
end;
  • Gunakan Nama Kolom Generik (kueri parsing hanya saat runtime)

Menyetel sumber ke jenis ini akan memungkinkan Anda menggunakan pendekatan yang lebih dinamis. Dengan mengubah setelan laporan ke jenis penguraian ini, apex hanya akan menghasilkan sejumlah kolom dalam metadatanya tanpa secara langsung dikaitkan dengan kueri yang sebenarnya. Hanya akan ada kolom dengan 'COL1', 'COL2', 'COL3',...
Jalankan laporan. Bekerja dengan baik. Sekarang masukkan beberapa data lagi.

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;

Jalankan laporan. Berfungsi dengan baik.
Namun, kekusutan di sini adalah nama kolom. Mereka tidak terlalu dinamis, dengan nama jelek mereka. Anda dapat mengedit kolom, tentu saja, tetapi tidak dinamis. Tidak ada kelas yang ditampilkan atau apa pun, Anda juga tidak dapat mengatur tajuknya dengan andal menjadi satu. Sekali lagi ini masuk akal:metadata ada di sana, tetapi statis. Ini bisa bekerja untuk Anda jika Anda senang dengan pendekatan ini.
Namun Anda bisa mengatasinya. Di "Atribut Laporan" laporan, Anda dapat memilih "Jenis Judul". Semuanya statis, tentu saja mengharapkan "PL/SQL"! Di sini Anda dapat menulis badan fungsi (atau cukup panggil fungsi) yang akan mengembalikan header kolom!

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;

Solusi pihak ketiga

Gunakan XML

Saya sendiri telah memilih untuk menggunakan kata kunci XML sebelumnya. Saya menggunakan pivot untuk memastikan saya memiliki nilai untuk semua baris dan kolom, lalu membacanya lagi dengan XMLTABLE , lalu buat satu XMLTYPE kolom, membuat serial ke CLOB .
Ini mungkin agak canggih, tapi ini adalah teknik yang telah saya gunakan beberapa kali sejauh ini, dengan hasil yang baik. Ini cepat, asalkan data dasar tidak terlalu besar, dan itu hanya satu panggilan sql, jadi tidak banyak sakelar konteks. Saya telah menggunakannya dengan data CUBE juga, dan itu berfungsi dengan baik.
(catatan:kelas yang saya tambahkan pada elemen sesuai dengan kelas yang digunakan pada laporan klasik di tema 1, merah sederhana)

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;

Di APEX: baik, karena HTML telah dibuat, ini hanya dapat menjadi wilayah PLSQL yang memanggil fungsi paket dan mencetaknya menggunakan HTP.PRN .

(edit) Ada juga posting ini di forum OTN yang melakukan hal yang sama di sebagian besar, tetapi tidak menghasilkan judul dll, melainkan menggunakan fungsi apex:OTN:Laporan matriks

PLSQL

Atau, Anda bisa memilih untuk memilih rute plsql yang bagus. Anda dapat mengambil isi dari sql dinamis di atas, mengulangnya, dan mengeluarkan struktur tabel dengan menggunakan htp.prn panggilan. Keluarkan tajuk, dan keluarkan apa pun yang Anda inginkan. Untuk efek yang baik, tambahkan kelas pada elemen yang sesuai dengan tema yang Anda gunakan.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Mengambil nilai maksimum dari sql-vb.net

  2. Solusi untuk ORA-00997:penggunaan tipe data PANJANG secara ilegal

  3. Jauhkan pesanan dari klausa 'IN'

  4. Terapkan aplikasi .Net dengan Oracle Client 11

  5. Bagaimana cara mengembalikan daftar nilai alih-alih string saat menanyakan database Oracle menggunakan XPath?