Oracle
 sql >> डेटाबेस >  >> RDS >> Oracle

SQL पिवट के साथ निम्न क्वेरी संभव है?

उत्तर देने में थोड़ा समय लगा, लेकिन मुझे यह सब लिखना और उसका परीक्षण करना था!

डेटा जिसके साथ मैंने काम किया है:

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, जैसा कि यह अभी खड़ा है, सरल तरीके से स्तंभों की गतिशील संख्या की अनुमति नहीं देता है। यह केवल एक्सएमएल कीवर्ड के साथ इसकी अनुमति देता है, जिसके परिणामस्वरूप एक xmltype कॉलम होता है। यहां कुछ उत्कृष्ट दस्तावेज़ हैं। http://www.oracle-base .com/articles/11g/pivot-and-unpivot-operators-11gr1.php
उन्हें पहले पढ़ना हमेशा फायदेमंद होता है।

फिर कैसे करें?
खोज शुरू करने के बाद आपको एक ही चीज़ के बारे में सचमुच ढेर सारे प्रश्न मिलेंगे।

डायनामिक SQL

एक क्लासिक रिपोर्ट एक फ़ंक्शन बॉडी ले सकती है जो एक एसक्यूएल स्टेटमेंट को रिटर्न के रूप में लौटाती है। एक इंटरैक्टिव रिपोर्ट नहीं कर सकता। जैसा कि यह खड़ा है, एक IR प्रश्न से बाहर है क्योंकि यह बहुत अधिक मेटाडेटा पर निर्भर है।

उदाहरण के लिए, क्लासिक रिपोर्ट क्षेत्र स्रोत में इन प्रश्नों/plsql के साथ:

स्थिर धुरी

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

फ़ंक्शन बॉडी रिटर्निंग स्टेटमेंट

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;

हालांकि क्षेत्र स्रोत में सेटिंग्स पर ध्यान दें।

  • क्वेरी-विशिष्ट कॉलम नामों का उपयोग करें और क्वेरी की पुष्टि करें

यह मानक सेटिंग है। यह आपकी क्वेरी को पार्स करेगा और फिर क्वेरी में मिले कॉलम को रिपोर्ट मेटाडेटा में संग्रहीत करेगा। यदि आप आगे बढ़ते हैं और उपरोक्त plsql कोड के साथ एक रिपोर्ट बनाते हैं, तो आप देख सकते हैं कि एपेक्स ने क्वेरी को पार्स किया है और सही कॉलम असाइन किए हैं। इस दृष्टिकोण में जो गलत है वह यह है कि मेटाडेटा स्थिर है। रिपोर्ट के मेटाडेटा को हर बार रिपोर्ट के चलने पर ताज़ा नहीं किया जाता है।
डेटा में एक और वर्ग जोड़कर इसे काफी सरलता से सिद्ध किया जा सकता है।

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;

रिपोर्ट संपादित किए बिना पृष्ठ चलाएँ! संपादन और सहेजना मेटाडेटा को पुन:उत्पन्न करेगा, जो स्पष्ट रूप से एक व्यवहार्य तरीका नहीं है। डेटा वैसे भी बदल जाएगा, और आप हर बार रिपोर्ट मेटाडेटा में जाकर सहेज नहीं सकते।

--cleanup
begin
delete from class where id = 3;
delete from class_meeting where id = 5;
delete from meeting_attendance where id = 10;
end;
  • सामान्य कॉलम नामों का उपयोग करें (केवल रनटाइम पर क्वेरी पार्स करें)

स्रोत को इस प्रकार पर सेट करने से आप अधिक गतिशील दृष्टिकोण का उपयोग कर सकेंगे। रिपोर्ट की सेटिंग को इस प्रकार की पार्सिंग में बदलकर, एपेक्स वास्तविक क्वेरी से सीधे जुड़े बिना अपने मेटाडेटा में केवल कॉलम की मात्रा उत्पन्न करेगा। बस 'COL1', 'COL2', 'COL3' वाले कॉलम होंगे,...
रिपोर्ट चलाएँ। ठीक काम करता है। अब फिर से कुछ डेटा डालें।

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;

रिपोर्ट चलाएँ। ठीक काम करता है।
हालाँकि, यहाँ किंक कॉलम नाम हैं। वे वास्तव में इतने गतिशील नहीं हैं, उनके बदसूरत नामों के साथ। आप निश्चित रूप से कॉलम संपादित कर सकते हैं, लेकिन वे गतिशील नहीं हैं। कोई वर्ग प्रदर्शित नहीं हो रहा है या कुछ भी नहीं है, न ही आप विश्वसनीय रूप से उनके शीर्षलेखों को एक पर सेट कर सकते हैं। फिर से यह समझ में आता है:मेटाडेटा है, लेकिन यह स्थिर है। यदि आप इस दृष्टिकोण से खुश हैं तो यह आपके काम आ सकता है।
हालांकि आप इससे निपट सकते हैं। रिपोर्ट की "रिपोर्ट विशेषताएँ" में, आप "शीर्षक प्रकार" का चयन कर सकते हैं। वे सभी स्थिर हैं, निश्चित रूप से "पीएल/एसक्यूएल" की अपेक्षा करें! यहां आप एक फंक्शन बॉडी लिख सकते हैं (या सिर्फ एक फंक्शन को कॉल करें) जो कॉलम हेडर लौटाएगा!

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;

तृतीय पक्ष समाधान

  • 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/
    अपेक्स में: हालांकि गतिशील धुरी स्थापित करने के बाद अधिक सरल है, शीर्ष में सेटअप वही रहता है जैसे आप गतिशील एसक्यूएल का उपयोग करना चाहते हैं। सामान्य कॉलम नामों वाली क्लासिक रिपोर्ट का उपयोग करें।
    मैं यहां अधिक विवरण में नहीं जा रहा हूं। मेरे पास यह पैकेज एटीएम स्थापित नहीं है। यह होना अच्छा है, लेकिन इस परिदृश्य में यह उतना मददगार नहीं हो सकता है। यह विशुद्ध रूप से आपको एक गतिशील धुरी को अधिक संक्षिप्त तरीके से लिखने की अनुमति देता है, लेकिन चीजों के शीर्ष पर ज्यादा मदद नहीं करता है। जैसा कि मैंने ऊपर दिखाया है, शीर्ष रिपोर्ट के गतिशील कॉलम और स्थिर मेटाडेटा यहां सीमित कारक हैं।

XML का उपयोग करें

मैंने स्वयं पहले XML कीवर्ड का उपयोग करने का विकल्प चुना है। मैं यह सुनिश्चित करने के लिए पिवट का उपयोग करता हूं कि मेरे पास सभी पंक्तियों और स्तंभों के लिए मान हैं, फिर इसे XMLTABLE के साथ फिर से पढ़ें , और फिर एक XMLTYPE . बनाना कॉलम, इसे CLOB . पर क्रमांकित करता है .
यह थोड़ा उन्नत हो सकता है, लेकिन यह एक ऐसी तकनीक है जिसका मैंने अब तक दो बार उपयोग किया है, जिसके अच्छे परिणाम मिले हैं। यह तेज़ है, बशर्ते आधार डेटा बहुत बड़ा न हो, और यह केवल एक sql कॉल है, इसलिए बहुत सारे संदर्भ स्विच नहीं हैं। मैंने इसे CUBE'd डेटा के साथ भी उपयोग किया है, और यह बहुत अच्छा काम करता है।
(नोट:मैंने तत्वों पर जो कक्षाएं जोड़ी हैं, वे थीम 1 में क्लासिक रिपोर्ट पर उपयोग की जाने वाली कक्षाओं के अनुरूप हैं, साधारण लाल)

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;

अपेक्स में: ठीक है, चूंकि HTML का निर्माण किया गया है, यह केवल एक PLSQL क्षेत्र हो सकता है जो पैकेज फ़ंक्शन को कॉल करता है और HTP.PRN का उपयोग करके इसे प्रिंट करता है। .

(संपादित करें) ओटीएन फोरम पर यह पोस्ट भी है जो एक बड़े हिस्से में ऐसा ही करता है, लेकिन शीर्ष कार्यात्मकताओं का उपयोग करके शीर्षक आदि उत्पन्न नहीं करता है:OTN:मैट्रिक्स रिपोर्ट

PLSQL

वैकल्पिक रूप से, आप केवल अच्छे ol' plsql मार्ग पर जाने का विकल्प चुन सकते हैं। आप ऊपर दिए गए डायनेमिक sql से बॉडी ले सकते हैं, उस पर लूप कर सकते हैं, और htp.prn का उपयोग करके एक टेबल स्ट्रक्चर तैयार कर सकते हैं। कॉल। हेडर बाहर रखें, और जो कुछ भी आप चाहते हैं उसे बाहर कर दें। अच्छे प्रभाव के लिए, आपके द्वारा उपयोग की जा रही थीम से मेल खाने वाले तत्वों पर कक्षाएं जोड़ें।



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. PLSQL गतिशील क्वेरी

  2. Oracle - वैकल्पिक मापदंडों के साथ सूचकांक का उपयोग

  3. टैब-पृथक डेटा फ़ाइल उत्पन्न करने के लिए SQLPlus सेटिंग्स

  4. Oracle में एक वैश्विक अस्थायी तालिका बनाना

  5. समस्याएँ जब sql कमांड में उपयोगकर्ता इनपुट डेटा