Saya ingin mengusulkan pendekatan yang berbeda menggunakan fungsi tabel PIPELINED. Ini agak mirip dengan teknik XMLTABLE, kecuali Anda menyediakan fungsi kustom Anda sendiri untuk memisahkan string karakter:
-- 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));
Hasil:
NAME PROJECT ERROR
---------- ---------- --------------------
108 test Err1
108 test Err2
108 test Err3
109 test2 Err1
Masalah dengan jenis pendekatan ini adalah sering kali pengoptimal tidak mengetahui kardinalitas fungsi tabel dan harus membuat perkiraan. Ini bisa berpotensi membahayakan rencana eksekusi Anda, jadi solusi ini dapat diperpanjang untuk menyediakan statistik eksekusi untuk pengoptimal.
Anda dapat melihat estimasi pengoptimal ini dengan menjalankan EXPLAIN PLAN pada kueri di atas:
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 |
----------------------------------------------------------------------------------------------
Meskipun koleksi hanya memiliki 3 nilai, pengoptimal memperkirakan 8168 baris untuknya (nilai default). Ini mungkin tampak tidak relevan pada awalnya, tetapi mungkin cukup bagi pengoptimal untuk memutuskan rencana yang kurang optimal.
Solusinya adalah dengan menggunakan ekstensi pengoptimal untuk memberikan statistik pengumpulan:
-- 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;
Menguji rencana eksekusi yang dihasilkan:
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 |
----------------------------------------------------------------------------------------------
Seperti yang Anda lihat, kardinalitas pada denah di atas bukanlah nilai tebakan 8196 lagi. Itu masih tidak benar karena kita mengirimkan kolom alih-alih string literal ke fungsi.
Beberapa penyesuaian pada kode fungsi akan diperlukan untuk memberikan perkiraan yang lebih dekat dalam kasus khusus ini, tetapi saya pikir konsep keseluruhan cukup banyak dijelaskan di sini.
Fungsi str2tbl yang digunakan dalam jawaban ini pada awalnya dikembangkan oleh Tom Kyte: https://asktom.oracle.com/pls/asktom/f?p=100:11 WindowsFAQ.ru::::P11_QUESTION_ID:110612348061
Konsep mengaitkan statistik dengan jenis objek dapat dieksplorasi lebih lanjut dengan membaca artikel ini:
http://www.oracle-developer.net/display.php?id=427
Teknik yang dijelaskan di sini bekerja dalam 10g +.
REGEXP
,XMLTABLE
danMODEL
klausa, lihat Memisahkan string yang dipisahkan koma dalam tabel menggunakan Oracle SQL