Würden Sie die PIPELINED-Funktion verwenden, um Ihr Ziel zu erreichen?
Ich habe ein Beispiel für eine solche Funktion geschrieben. Das Beispiel basiert auf der Tabelle, Beispieldaten und PIVOT
Abfrage aus den Artikeln von Tom Kyte, die Sie auf seiner Website finden können:
Tom Kytes Artikel über PIVOT/UNPIVOT
Tom Kytes Artikel über PIPELINED-Funktionen
Das Beispiel funktioniert wie folgt.
Wir erstellen zwei Typen:
- t_pivot_test_obj - Typ, der Spalten enthält, die wir aus XML abrufen möchten
- t_pivot_test_obj_tab - verschachtelter Tabellentyp der obigen Objekte.
Dann erstellen wir eine PIPELINED-Funktion, die die Abfrage mit PIVOT
enthält , das XML generiert (damit Sie die Werte, über die Sie pivotieren möchten, nicht fest codieren müssen). Diese Funktion extrahiert Daten aus generiertem XML und übergibt (PIPEs) Zeilen an die aufrufende Abfrage, während sie generiert werden (on the fly – sie werden nicht alle auf einmal generiert, was für die Leistung wichtig ist).
Schließlich schreiben Sie eine Abfrage, die Datensätze aus dieser Funktion auswählt (am Ende ist ein Beispiel für eine solche Abfrage).
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())
;
Ausgabe:
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