sql >> Database >  >> RDS >> Oracle

Oracle pivot met subquery

Zou je overwegen om de PIPELINED-functie te gebruiken om je doel te bereiken?

Ik heb een voorbeeld van zo'n functie geschreven. Het voorbeeld is gebaseerd op de tabel, voorbeeldgegevens en PIVOT vraag uit de artikelen van Tom Kyte die u op zijn site kunt vinden:

Tom Kyte's artikel over PIVOT/UNPIVOT

Tom Kyte's artikel over PIPELINED-functies

Het voorbeeld werkt als volgt.

We maken twee soorten:

  • t_pivot_test_obj - type dat kolommen bevat die we uit XML willen ophalen
  • t_pivot_test_obj_tab - geneste tabeltype van bovenstaande objecten.

Vervolgens maken we een PIPELINED-functie die de query bevat met PIVOT , die XML genereert (zodat u de waarden die u wilt draaien niet hard hoeft te coderen). Deze functie extraheert gegevens uit gegenereerde XML en geeft (PIPE's) rijen door aan de aanroepende query terwijl ze worden gegenereerd (on-the-fly - ze worden niet allemaal tegelijk gegenereerd, wat belangrijk is voor de prestaties).

Ten slotte schrijf je een query die records uit die functie selecteert (aan het einde is een voorbeeld van zo'n query).

CREATE TABLE pivot_test (
  id            NUMBER,
  customer_id   NUMBER,
  product_code  VARCHAR2(5),
  quantity      NUMBER
);

INSERT INTO pivot_test VALUES (1, 1, 'A', 10);
INSERT INTO pivot_test VALUES (2, 1, 'B', 20);
INSERT INTO pivot_test VALUES (3, 1, 'C', 30);
INSERT INTO pivot_test VALUES (4, 2, 'A', 40);
INSERT INTO pivot_test VALUES (5, 2, 'C', 50);
INSERT INTO pivot_test VALUES (6, 3, 'A', 60);
INSERT INTO pivot_test VALUES (7, 3, 'B', 70);
INSERT INTO pivot_test VALUES (8, 3, 'C', 80);
INSERT INTO pivot_test VALUES (9, 3, 'D', 90);
INSERT INTO pivot_test VALUES (10, 4, 'A', 100);
COMMIT;

CREATE TYPE t_pivot_test_obj AS OBJECT (
  customer_id   NUMBER,
  product_code  VARCHAR2(5),
  sum_quantity  NUMBER
);
/

CREATE TYPE t_pivot_test_obj_tab IS TABLE OF t_pivot_test_obj;
/

CREATE OR REPLACE FUNCTION extract_from_xml RETURN t_pivot_test_obj_tab PIPELINED
AS
  v_xml XMLTYPE;
  v_item_xml XMLTYPE;
  v_index NUMBER;
  v_sum_quantity NUMBER;

  CURSOR c_customer_items IS
    SELECT customer_id, product_code_xml
      FROM (SELECT customer_id, product_code, quantity
              FROM pivot_test)
      PIVOT XML (SUM(quantity) AS sum_quantity FOR (product_code) IN (SELECT DISTINCT product_code 
                                                                      FROM pivot_test));
BEGIN
  -- loop through all records returned by query with PIVOT
  FOR v_rec IN c_customer_items
  LOOP
    v_xml := v_rec.product_code_xml;
    v_index := 1;

    -- loop through all ITEM elements for each customer
    LOOP
      v_item_xml := v_xml.EXTRACT('/PivotSet/item[' || v_index || ']');

      EXIT WHEN v_item_xml IS NULL;

      v_index := v_index + 1;

      IF v_item_xml.EXTRACT('/item/column[@name="SUM_QUANTITY"]/text()') IS NOT NULL THEN
        v_sum_quantity := v_item_xml.EXTRACT('/item/column[@name="SUM_QUANTITY"]/text()').getNumberVal();
      ELSE
        v_sum_quantity := 0;
      END IF;

      -- finally, for each customer and item - PIPE the row to the calling query
      PIPE ROW(t_pivot_test_obj(v_rec.customer_id,
                                v_item_xml.EXTRACT('/item/column[@name="PRODUCT_CODE"]/text()').getStringVal(),
                                v_sum_quantity));
    END LOOP;
  END LOOP;
END;
/

SELECT customer_id, product_code, sum_quantity
  FROM TABLE(extract_from_xml())
;

Uitgang:

CUSTOMER_ID            PRODUCT_CODE SUM_QUANTITY           
---------------------- ------------ ---------------------- 
1                      A            10                     
1                      B            20                     
1                      C            30                     
1                      D            0                      
2                      A            40                     
2                      B            0                      
2                      C            50                     
2                      D            0                      
3                      A            60                     
3                      B            70                     
3                      C            80                     
3                      D            90                     
4                      A            100                    
4                      B            0                      
4                      C            0                      
4                      D            0                      

16 rows selected


  1. MySql-fout:1364 veld 'display_name' heeft geen standaardwaarde

  2. Standaardbeperking toevoegen aan bestaande kolommen in SQL Server-tabel - SQL Server / TSQL-zelfstudie, deel 91

  3. Query's loggen in PostgreSQL

  4. Snelste manier om alle databases in SQL Server weer te geven met behulp van T-SQL