Oracle
 sql >> Cơ Sở Dữ Liệu >  >> RDS >> Oracle

Truy vấn sau có khả thi với SQL Pivot không?

Phải mất một lúc để trả lời, nhưng tôi phải viết tất cả điều này và kiểm tra nó!

Dữ liệu tôi đã làm việc với:

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, như hiện tại, không cho phép một số cột động theo cách đơn giản. Nó chỉ cho phép điều này với từ khóa XML, dẫn đến một cột xmltype. Dưới đây là một số tài liệu tuyệt vời. http://www.oracle-base .com / posts / 11g / pivot-and-univot-operator-11gr1.php
Luôn luôn có lợi khi đọc những thứ đó trước.

Vậy thì làm thế nào?
Bạn sẽ tìm thấy rất nhiều câu hỏi về cùng một thứ khi bạn bắt đầu tìm kiếm.

SQL động

Một báo cáo cổ điển có thể lấy một phần thân hàm trả về câu lệnh sql dưới dạng trả về. Một báo cáo tương tác không thể. Như hiện tại, IR không nằm ngoài câu hỏi vì nó quá phụ thuộc vào siêu dữ liệu.

Ví dụ:với các truy vấn / plsql này trong nguồn khu vực báo cáo cổ điển:

trục tĩnh

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

câu lệnh trả về nội dung hàm

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;

Tuy nhiên, hãy lưu ý các cài đặt trong nguồn khu vực.

  • Sử dụng Tên cột dành riêng cho Truy vấn và Xác thực Truy vấn

Đây là cài đặt tiêu chuẩn. Nó sẽ phân tích cú pháp truy vấn của bạn và sau đó lưu trữ các cột được tìm thấy trong truy vấn trong siêu dữ liệu báo cáo. Nếu bạn tiếp tục và tạo một báo cáo với mã plsql ở trên, bạn có thể thấy rằng apex đã phân tích cú pháp truy vấn và đã chỉ định các cột chính xác. Điều sai với cách tiếp cận này là siêu dữ liệu đó là tĩnh. Siêu dữ liệu của báo cáo không được làm mới mỗi khi chạy báo cáo.
Điều này có thể được chứng minh khá đơn giản bằng cách thêm một lớp khác vào dữ liệu.

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;

Chạy trang mà không cần chỉnh sửa báo cáo! Chỉnh sửa và lưu sẽ tạo lại siêu dữ liệu, đây rõ ràng không phải là một phương pháp khả thi. Dữ liệu vẫn sẽ thay đổi và bạn không thể truy cập và lưu siêu dữ liệu báo cáo mọi lúc.

--cleanup
begin
delete from class where id = 3;
delete from class_meeting where id = 5;
delete from meeting_attendance where id = 10;
end;
  • Sử dụng Tên cột Chung (chỉ phân tích cú pháp truy vấn trong thời gian chạy)

Đặt nguồn thành loại này sẽ cho phép bạn sử dụng một cách tiếp cận năng động hơn. Bằng cách thay đổi cài đặt của báo cáo thành loại phân tích cú pháp này, apex sẽ chỉ tạo một lượng cột trong siêu dữ liệu của nó mà không được liên kết trực tiếp với truy vấn thực tế. Sẽ chỉ có các cột có 'COL1', 'COL2', 'COL3', ...
Chạy báo cáo. Hoạt động tốt. Bây giờ hãy chèn lại một số dữ liệu.

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;

Chạy báo cáo. Hoạt động tốt.
Tuy nhiên, đường gấp khúc ở đây là tên cột. Chúng không thực sự năng động như vậy, với cái tên xấu xí của chúng. Bạn có thể chỉnh sửa các cột, chắc chắn, nhưng chúng không động. Không có lớp nào được hiển thị hoặc bất kỳ thứ gì, bạn cũng không thể đặt tiêu đề của chúng thành một lớp một cách đáng tin cậy. Một lần nữa điều này có ý nghĩa:siêu dữ liệu ở đó, nhưng nó ở trạng thái tĩnh. Nó có thể hiệu quả với bạn nếu bạn hài lòng với cách tiếp cận này.
Tuy nhiên, bạn có thể giải quyết vấn đề này. Trong "Thuộc tính Báo cáo" của báo cáo, bạn có thể chọn "Loại tiêu đề". Tất cả chúng đều tĩnh, tất nhiên là mong đợi cho "PL / SQL"! Tại đây, bạn có thể viết một nội dung hàm (hoặc chỉ cần gọi một hàm) sẽ trả về các tiêu đề cột!

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;

Giải pháp của bên thứ ba

Sử dụng XML

Bản thân tôi đã chọn sử dụng từ khóa XML trước đây. Tôi sử dụng pivot để đảm bảo rằng tôi có các giá trị cho tất cả các hàng và cột, sau đó đọc lại bằng XMLTABLE và sau đó tạo một XMLTYPE , sắp xếp nó thành CLOB .
Điều này có thể hơi nâng cao, nhưng đó là một kỹ thuật mà tôi đã sử dụng một vài lần cho đến nay, với kết quả tốt. Nó nhanh, miễn là dữ liệu cơ sở không quá lớn và nó chỉ là một cuộc gọi sql, vì vậy không có nhiều chuyển đổi ngữ cảnh. Tôi cũng đã sử dụng nó với dữ liệu CUBE'd và nó hoạt động rất tốt.
(lưu ý:các lớp tôi đã thêm vào các phần tử tương ứng với các lớp được sử dụng trên các báo cáo cổ điển trong chủ đề 1, màu đỏ đơn giản)

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;

Trong APEX: tốt, vì HTML đã được xây dựng, đây chỉ có thể là một vùng PLSQL gọi hàm gói và in nó bằng cách sử dụng HTP.PRN .

(sửa) Cũng có bài đăng này trên diễn đàn OTN, phần lớn cũng thực hiện như vậy, nhưng không tạo tiêu đề, v.v., thay vì sử dụng các hàm apex: OTN:Báo cáo ma trận

PLSQL

Ngoài ra, bạn có thể chọn đi theo con đường tốt. Bạn có thể lấy phần nội dung từ sql động ở trên, lặp qua nó và đưa ra cấu trúc bảng bằng cách sử dụng htp.prn cuộc gọi. Đưa ra các tiêu đề và đưa ra bất cứ điều gì khác bạn muốn. Để có hiệu ứng tốt, hãy thêm các lớp trên các phần tử tương ứng với chủ đề bạn đang sử dụng.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Truy vấn động với HibernateCritera API &Oracle - hiệu suất

  2. ORA-01264:Không thể tạo tên tệp logfile

  3. Tạo ứng dụng Java trong Oracle JDeveloper, Phần 1

  4. Không thể định vị Oracle.DataAccess.dll mặc dù nó tồn tại

  5. Trình quản lý đồng thời Oracle