Ich möchte einen anderen Ansatz mit einer PIPELINED-Tabellenfunktion vorschlagen. Es ähnelt etwas der Technik von XMLTABLE, außer dass Sie Ihre eigene benutzerdefinierte Funktion zum Teilen der Zeichenfolge bereitstellen:
-- Create a collection type to hold the results
CREATE OR REPLACE TYPE typ_str2tbl_nst AS TABLE OF VARCHAR2(30);
/
-- Split the string according to the specified delimiter
CREATE OR REPLACE FUNCTION str2tbl (
p_string VARCHAR2,
p_delimiter CHAR DEFAULT ','
)
RETURN typ_str2tbl_nst PIPELINED
AS
l_tmp VARCHAR2(32000) := p_string || p_delimiter;
l_pos NUMBER;
BEGIN
LOOP
l_pos := INSTR( l_tmp, p_delimiter );
EXIT WHEN NVL( l_pos, 0 ) = 0;
PIPE ROW ( RTRIM( LTRIM( SUBSTR( l_tmp, 1, l_pos-1) ) ) );
l_tmp := SUBSTR( l_tmp, l_pos+1 );
END LOOP;
END str2tbl;
/
-- The problem solution
SELECT name,
project,
TRIM(COLUMN_VALUE) error
FROM t, TABLE(str2tbl(error));
Ergebnisse:
NAME PROJECT ERROR
---------- ---------- --------------------
108 test Err1
108 test Err2
108 test Err3
109 test2 Err1
Das Problem bei dieser Art von Ansatz ist, dass der Optimierer häufig die Kardinalität der Tabellenfunktion nicht kennt und eine Vermutung anstellen muss. Dies kann möglicherweise schädlich für Ihre Ausführungspläne sein. Daher kann diese Lösung erweitert werden, um Ausführungsstatistiken für das Optimierungsprogramm bereitzustellen.
Sie können diese Optimierungsschätzung anzeigen, indem Sie einen EXPLAIN PLAN für die obige Abfrage ausführen:
Execution Plan
----------------------------------------------------------
Plan hash value: 2402555806
----------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 16336 | 366K| 59 (0)| 00:00:01 |
| 1 | NESTED LOOPS | | 16336 | 366K| 59 (0)| 00:00:01 |
| 2 | TABLE ACCESS FULL | T | 2 | 42 | 3 (0)| 00:00:01 |
| 3 | COLLECTION ITERATOR PICKLER FETCH| STR2TBL | 8168 | 16336 | 28 (0)| 00:00:01 |
----------------------------------------------------------------------------------------------
Obwohl die Sammlung nur 3 Werte enthält, hat der Optimierer 8168 Zeilen dafür geschätzt (Standardwert). Dies mag zunächst irrelevant erscheinen, aber es kann für den Optimierer ausreichend sein, sich für einen suboptimalen Plan zu entscheiden.
Die Lösung besteht darin, die Optimierungserweiterungen zu verwenden, um Statistiken für die Sammlung bereitzustellen:
-- Create the optimizer interface to the str2tbl function
CREATE OR REPLACE TYPE typ_str2tbl_stats AS OBJECT (
dummy NUMBER,
STATIC FUNCTION ODCIGetInterfaces ( p_interfaces OUT SYS.ODCIObjectList )
RETURN NUMBER,
STATIC FUNCTION ODCIStatsTableFunction ( p_function IN SYS.ODCIFuncInfo,
p_stats OUT SYS.ODCITabFuncStats,
p_args IN SYS.ODCIArgDescList,
p_string IN VARCHAR2,
p_delimiter IN CHAR DEFAULT ',' )
RETURN NUMBER
);
/
-- Optimizer interface implementation
CREATE OR REPLACE TYPE BODY typ_str2tbl_stats
AS
STATIC FUNCTION ODCIGetInterfaces ( p_interfaces OUT SYS.ODCIObjectList )
RETURN NUMBER
AS
BEGIN
p_interfaces := SYS.ODCIObjectList ( SYS.ODCIObject ('SYS', 'ODCISTATS2') );
RETURN ODCIConst.SUCCESS;
END ODCIGetInterfaces;
-- This function is responsible for returning the cardinality estimate
STATIC FUNCTION ODCIStatsTableFunction ( p_function IN SYS.ODCIFuncInfo,
p_stats OUT SYS.ODCITabFuncStats,
p_args IN SYS.ODCIArgDescList,
p_string IN VARCHAR2,
p_delimiter IN CHAR DEFAULT ',' )
RETURN NUMBER
AS
BEGIN
-- I'm using basically half the string lenght as an estimator for its cardinality
p_stats := SYS.ODCITabFuncStats( CEIL( LENGTH( p_string ) / 2 ) );
RETURN ODCIConst.SUCCESS;
END ODCIStatsTableFunction;
END;
/
-- Associate our optimizer extension with the PIPELINED function
ASSOCIATE STATISTICS WITH FUNCTIONS str2tbl USING typ_str2tbl_stats;
Testen des resultierenden Ausführungsplans:
Execution Plan
----------------------------------------------------------
Plan hash value: 2402555806
----------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 23 | 59 (0)| 00:00:01 |
| 1 | NESTED LOOPS | | 1 | 23 | 59 (0)| 00:00:01 |
| 2 | TABLE ACCESS FULL | T | 2 | 42 | 3 (0)| 00:00:01 |
| 3 | COLLECTION ITERATOR PICKLER FETCH| STR2TBL | 1 | 2 | 28 (0)| 00:00:01 |
----------------------------------------------------------------------------------------------
Wie Sie sehen können, ist die Kardinalität im obigen Plan nicht mehr der geschätzte Wert von 8196. Es ist immer noch nicht korrekt, da wir eine Spalte anstelle eines Zeichenfolgenliteral an die Funktion übergeben.
In diesem speziellen Fall wäre eine gewisse Anpassung des Funktionscodes erforderlich, um eine genauere Schätzung vorzunehmen, aber ich denke, das Gesamtkonzept wird hier ziemlich genau erläutert.
Die in dieser Antwort verwendete str2tbl-Funktion wurde ursprünglich von Tom Kyte entwickelt:
https://asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:110612348061
Das Konzept der Zuordnung von Statistiken zu Objekttypen kann in diesem Artikel näher erläutert werden:
http://www.oracle-developer.net/display.php?id=427
Die hier beschriebene Technik funktioniert in 10g +.
REGEXP
,XMLTABLE
undMODEL
Klausel findet Split Komma getrennte Zeichenfolge in einer Tabelle mit Oracle SQL