这是另一个问题答案的扩展:https://stackoverflow.com/a/64913702/4351488。在提供的解决方案中,处理原始数据类型,但我还需要迭代处理嵌套集合。为了简单起见,该示例仅提供命名集合的单个实例,但其想法是该实现支持深度嵌套集合。
采用以下数据类型:
CREATE TYPE book_page is OBJECT(
book_page number,
content varchar2(4000)
);
/
CREATE TYPE table_of_book_pages IS TABLE OF book_page;
/
CREATE TYPE book is OBJECT(
title varchar(50),
author varchar(50),
subject varchar(100),
book_id number,
first_published DATE,
book_pages table_of_book_pages
);
/
CREATE TYPE table_of_books IS TABLE OF book;
/
使用原答案中提供的反射包
CREATE PACKAGE reflection IS
TYPE type_info IS RECORD(
prec PLS_INTEGER,
scale PLS_INTEGER,
len PLS_INTEGER,
csid PLS_INTEGER,
csfrm PLS_INTEGER,
schema_name VARCHAR2(30),
type_name VARCHAR2(30),
version VARCHAR2(100),
count PLS_INTEGER
);
TYPE attr_info IS RECORD(
prec PLS_INTEGER,
scale PLS_INTEGER,
len PLS_INTEGER,
csid PLS_INTEGER,
csfrm PLS_INTEGER,
attr_elt_type ANYTYPE,
aname VARCHAR2(30)
);
FUNCTION get_size(
p_anydata IN ANYDATA
) RETURN PLS_INTEGER;
FUNCTION get_attr_name_at(
p_anydata IN ANYDATA,
p_index IN PLS_INTEGER DEFAULT 1
) RETURN VARCHAR2;
FUNCTION get_attr_value_at(
p_anydata IN ANYDATA,
p_index IN PLS_INTEGER DEFAULT 1
) RETURN VARCHAR2;
END;
/
CREATE OR REPLACE PACKAGE BODY reflection IS
FUNCTION get_type(
p_anydata IN ANYDATA
) RETURN ANYTYPE
IS
v_typeid PLS_INTEGER;
v_anytype ANYTYPE;
v_type_info REFLECTION.TYPE_INFO;
BEGIN
v_typeid := p_anydata.GetType( typ => v_anytype );
RETURN v_anytype;
END;
FUNCTION get_info(
p_anytype IN ANYTYPE
) RETURN type_info
IS
v_typeid PLS_INTEGER;
v_type_info REFLECTION.TYPE_INFO;
BEGIN
v_typeid := p_anytype.GetInfo (
v_type_info.prec,
v_type_info.scale,
v_type_info.len,
v_type_info.csid,
v_type_info.csfrm,
v_type_info.schema_name,
v_type_info.type_name,
v_type_info.version,
v_type_info.count
);
IF v_typeid <> DBMS_TYPES.TYPECODE_OBJECT THEN
RAISE_APPLICATION_ERROR( -20000, 'Not an object.' );
END IF;
RETURN v_type_info;
END;
FUNCTION get_size(
p_anydata IN ANYDATA
) RETURN PLS_INTEGER
IS
BEGIN
RETURN Get_Info( Get_Type( p_anydata ) ).COUNT;
END;
FUNCTION get_attr_name_at(
p_anydata IN ANYDATA,
p_index IN PLS_INTEGER DEFAULT 1
) RETURN VARCHAR2
IS
v_anydata ANYDATA := p_anydata;
v_anytype ANYTYPE;
v_type_info REFLECTION.TYPE_INFO;
v_output VARCHAR2(4000);
v_attr_typeid PLS_INTEGER;
v_attr_info REFLECTION.ATTR_INFO;
BEGIN
v_anytype := Get_Type( v_anydata );
v_type_info := Get_Info( v_anytype );
IF p_index < 1 OR p_index > v_type_info.COUNT THEN
RETURN NULL;
END IF;
v_anydata.PIECEWISE;
v_attr_typeid := v_anytype.getAttrElemInfo(
pos => p_index,
prec => v_attr_info.prec,
scale => v_attr_info.scale,
len => v_attr_info.len,
csid => v_attr_info.csid,
csfrm => v_attr_info.csfrm,
attr_elt_type => v_attr_info.attr_elt_type,
aname => v_attr_info.aname
);
RETURN v_attr_info.aname;
END;
FUNCTION get_attr_value_at(
p_anydata IN ANYDATA,
p_index IN PLS_INTEGER DEFAULT 1
) RETURN VARCHAR2
IS
v_anydata ANYDATA := p_anydata;
v_anytype ANYTYPE;
v_type_info REFLECTION.TYPE_INFO;
v_output VARCHAR2(4000);
BEGIN
v_anytype := Get_Type( v_anydata );
v_type_info := Get_Info( v_anytype );
IF p_index < 1 OR p_index > v_type_info.COUNT THEN
RETURN NULL;
END IF;
v_anydata.PIECEWISE;
FOR i IN 1 .. p_index LOOP
DECLARE
v_attr_typeid PLS_INTEGER;
v_attr_info REFLECTION.ATTR_INFO;
v_result_code PLS_INTEGER;
BEGIN
v_attr_typeid := v_anytype.getAttrElemInfo(
pos => i,
prec => v_attr_info.prec,
scale => v_attr_info.scale,
len => v_attr_info.len,
csid => v_attr_info.csid,
csfrm => v_attr_info.csfrm,
attr_elt_type => v_attr_info.attr_elt_type,
aname => v_attr_info.aname
);
IF DEBUG THEN
DBMS_OUTPUT.PUT_LINE(
'Attribute ' || i || ': '
|| v_attr_info.aname
|| ' (type ' || v_attr_typeid || ')'
);
END IF;
CASE v_attr_typeid
WHEN DBMS_TYPES.TYPECODE_NUMBER THEN
DECLARE
v_value NUMBER;
BEGIN
v_result_code := v_anydata.GetNumber( v_value );
IF i = p_index THEN
RETURN TO_CHAR( v_value );
END IF;
END;
WHEN DBMS_TYPES.TYPECODE_VARCHAR2 THEN
DECLARE
v_value VARCHAR2(4000);
BEGIN
v_result_code := v_anydata.GetVarchar2( v_value );
IF i = p_index THEN
RETURN v_value;
END IF;
END;
WHEN DBMS_TYPES.TYPECODE_DATE THEN
DECLARE
v_value DATE;
BEGIN
v_result_code := v_anydata.GetDate( v_value );
IF i = p_index THEN
RETURN TO_CHAR( v_value, 'YYYY-MM-DD HH24:MI:SS' );
END IF;
END;
ELSE
RETURN NULL;
END CASE;
END;
END LOOP;
RETURN NULL;
END;
END;
/
对于以下输入
DECLARE
list_of_books table_of_books;
idx PLS_INTEGER := 1;
p_anydata ANYDATA;
p_attr_name VARCHAR2(30);
p_attr_value VARCHAR2(4000);
BEGIN
dbms_output.enable;
list_of_books := table_of_books(
book(
'First book',
'Me',
'Simple Ones',
94321,
DATE '1970-01-01',
table_of_book_pages(
book_page(
1,
'This is the page 1 of the First book'
),
book_page(
2,
'This is the page 2 of the First book'
)
)
),
book(
'Second book',
'You',
'Intermediate Ones',
55555,
DATE '2020-01-01',
table_of_book_pages(
book_page(
1,
'This is the page 1 of the Second book'
)
)
),
book(
'Third book',
NULL,
'Advanced Ones',
77777,
DATE '2099-12-31' + INTERVAL '0 23:59:59' DAY TO SECOND,
table_of_book_pages(
book_page(
1,
'This is the page 1 of the Third book'
),
book_page(
2,
'This is the page 2 of the Third book'
),
book_page(
3,
'This is the page 3 of the Third book'
)
)
)
);
FOR book_no IN 1 .. list_of_books.COUNT LOOP
p_anydata := ANYDATA.ConvertObject( list_of_books(book_no) );
DBMS_OUTPUT.PUT_LINE( 'Book ' || book_no || ':' );
FOR attr_no IN 1 .. REFLECTION.get_size( p_anydata ) LOOP
p_attr_name := REFLECTION.get_attr_name_at( p_anydata, attr_no );
p_attr_value := REFLECTION.get_attr_value_at( p_anydata, attr_no );
DBMS_OUTPUT.PUT_LINE( ' ' || p_attr_name || ': ' || p_attr_value );
-- how can I iterate book_pages here without explicitly declaring a table_of_book_pages type variable?
END LOOP;
END LOOP;
END;
/
我期望以下输出:
Book 1:
TITLE: First book
AUTHOR: Me
SUBJECT: Simple Ones
BOOK_ID: 94321
FIRST_PUBLISHED: 1970-01-01 00:00:00
BOOK_PAGES:
PAGE:
PAGE_ID: 1,
CONTENT: This is the page 1 of the First book
PAGE:
PAGE_ID: 2,
CONTENT: This is the page 2 of the First book
Book 2:
TITLE: Second book
AUTHOR: You
SUBJECT: Intermediate Ones
BOOK_ID: 55555
FIRST_PUBLISHED: 2020-01-01 00:00:00
BOOK_PAGES:
PAGE:
PAGE_ID: 1,
CONTENT: This is the page 1 of the Second book
Book 3:
TITLE: Third book
AUTHOR:
SUBJECT: Advanced Ones
BOOK_ID: 77777
FIRST_PUBLISHED: 2099-12-31 23:59:59
BOOK_PAGES:
PAGE:
PAGE_ID: 1,
CONTENT: This is the page 1 of the Third book
PAGE:
PAGE_ID: 2,
CONTENT: This is the page 2 of the Third book
PAGE:
PAGE_ID: 3,
CONTENT: This is the page 3 of the Third book
实现的目标是以通用方式处理此问题,支持任何类型的命名集合。如果不显式声明集合数据类型的变量,这可以实现吗?
如果不显式声明集合数据类型的变量,这可以实现吗?
ANYDATA
类型有方法:
MEMBER FUNCTION GetCollection(
self IN ANYDATA,
col OUT NOCOPY "<collection_type>")
RETURN PLS_INTEGER;
MEMBER FUNCTION GetObject(
self IN ANYDATA,
obj OUT NOCOPY "<object_type>")
RETURN PLS_INTEGER;
它们都要求您传入具有正确数据类型的变量,因此似乎没有任何方法可以通过
ANYDATA
对象执行分段迭代以以通用方式检索对象或集合属性。