Tôi muốn đề xuất một cách tiếp cận khác bằng cách sử dụng hàm bảng PIPELINED. Nó hơi giống với kỹ thuật của XMLTABLE, ngoại trừ việc bạn đang cung cấp hàm tùy chỉnh của riêng mình để tách chuỗi ký tự:
-- 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));
Các kết quả:
NAME PROJECT ERROR
---------- ---------- --------------------
108 test Err1
108 test Err2
108 test Err3
109 test2 Err1
Vấn đề với kiểu tiếp cận này là thường trình tối ưu hóa sẽ không biết bản chất của hàm bảng và nó sẽ phải đoán. Điều này có thể có hại cho kế hoạch thực thi của bạn, vì vậy giải pháp này có thể được mở rộng để cung cấp số liệu thống kê thực thi cho trình tối ưu hóa.
Bạn có thể xem ước tính của trình tối ưu hóa này bằng cách chạy KẾ HOẠCH GIẢI THÍCH trên truy vấn ở trên:
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 |
----------------------------------------------------------------------------------------------
Mặc dù bộ sưu tập chỉ có 3 giá trị, trình tối ưu hóa đã ước tính 8168 hàng cho nó (giá trị mặc định). Điều này thoạt đầu có vẻ không liên quan, nhưng nó có thể đủ để trình tối ưu hóa quyết định cho một kế hoạch phụ tối ưu.
Giải pháp là sử dụng các tiện ích mở rộng của trình tối ưu hóa để cung cấp số liệu thống kê cho bộ sưu tập:
-- 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;
Kiểm tra kế hoạch thực hiện kết quả:
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 |
----------------------------------------------------------------------------------------------
Như bạn có thể thấy cardinality trong kế hoạch ở trên không phải là giá trị được đoán 8196 nữa. Nó vẫn không chính xác vì chúng ta đang truyền một cột thay vì một chuỗi theo nghĩa đen cho hàm.
Một số điều chỉnh đối với mã chức năng sẽ là cần thiết để đưa ra một ước tính kỹ hơn trong trường hợp cụ thể này, nhưng tôi nghĩ rằng khái niệm tổng thể được giải thích khá nhiều ở đây.
Hàm str2tbl được sử dụng trong câu trả lời này ban đầu được phát triển bởi Tom Kyte:
https://asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:110612348061
Có thể khám phá thêm khái niệm kết hợp thống kê với các kiểu đối tượng bằng cách đọc bài viết này:
http://www.oracle-developer.net/display.php?id=427
Kỹ thuật được mô tả ở đây hoạt động trong 10g +.
REGEXP
,XMLTABLE
vàMODEL
khoản, xem Chia dấu phẩy chuỗi phân định trong một bảng sử dụng Oracle SQL