我正在使用Oracle对象数据类型来表示时间跨度或周期。而且我必须做一些涉及处理句点集合的操作。在SQL中迭代集合比在PL / SQL中快得多。
CREATE TYPE PERIOD AS OBJECT (
beginning DATE,
ending DATE,
... some member functions...);
CREATE TYPE PERIOD_TABLE AS TABLE OF PERIOD;
-- what I would like to do: where t.column_value is still a period type
SELECT (t.column_value).range_intersect(period2)
FROM TABLE(period_table1) t
WHERE pa_contains(period_table1, (t.column_value).prev()) = 0
AND pa_contains(period_table1, (t.column_value).next()) = 1
问题是TABLE()函数将对象分解为标量值,而我确实需要这些对象。我可以使用标量值来重新创建对象,但这会导致重新实例化对象的开销。并且这个时期被设计为子类,因此在尝试初始化它时会有额外的困难。
在SQL中是否有另一种方法可以不破坏我的对象?
答案 0 :(得分:0)
如果我正确理解你的问题,问题在于TABLE运算符的行为,它不返回对象表(包含属性和成员函数),而是一个简单的“标量”表(只有属性可访问) )。
我不确定你能用TABLE运算符做什么。以下是使用临时表而不是嵌套表(Oracle 10.2.0.1)的解决方法:
SQL> CREATE OR REPLACE TYPE PERIOD AS OBJECT (
2 beginning DATE,
3 ending DATE,
4 MEMBER FUNCTION range_intersect (p_period period) RETURN VARCHAR2
5 );
6 /
Type created
SQL> CREATE OR REPLACE TYPE BODY period IS
2 MEMBER FUNCTION range_intersect(p_period period) RETURN VARCHAR2 IS
3 BEGIN
4 RETURN CASE
5 WHEN p_period.beginning <= ending
6 AND p_period.ending >= beginning
7 THEN 'Y'
8 ELSE 'N'
9 END;
10 END range_intersect;
11 END;
12 /
Type body created
SQL> CREATE GLOBAL TEMPORARY TABLE period_table_tmp (
2 per period
3 );
Table created
使用此设置,我可以在SQL中查询表period_table_tmp
,就像它是period
对象的集合一样(即我可以看到成员函数):
SQL> INSERT INTO period_table_tmp
2 VALUES (period(DATE '2010-01-01', DATE '2010-01-31'));
1 row inserted
SQL> INSERT INTO period_table_tmp
2 VALUES (period(DATE '2010-02-01', DATE '2010-02-28'));
1 row inserted
SQL> SELECT t.per.beginning, t.per.ending,
2 t.per.range_intersect(period(DATE '2010-01-01',
3 DATE '2010-01-15')) intersec
4 FROM period_table_tmp t;
PER.BEGINNING PER.ENDING INTERSEC
------------- ----------- ---------
01/01/2010 31/01/2010 Y
01/02/2010 28/02/2010 N
答案 1 :(得分:0)
对不起,这是一个非常棘手的问题。但我终于找到了一种方法来使用我之前创建的一些工具来做到这一点。诀窍最终是迭代嵌套的数字表来获取每个元素。
所以第一件作品是我从Postgres无耻地借来的一系列发电机。还有其他方法可以生成数字,但这非常有效。
CREATE OR REPLACE FUNCTION generate_series(
p_start NUMBER,
p_end NUMBER
) RETURN NUMBER_TABLE PIPELINED IS
BEGIN
FOR i IN p_start .. p_end LOOP
PIPE ROW(i);
END LOOP;
RETURN;
END;
第二部分是能够在SQL中下标集合项。
CREATE OR REPLACE FUNCTION get_item(p1 PERIOD_TABLE, idx NUMBER)
RETURN PERIOD IS
BEGIN
RETURN p1(idx);
END;
最后把它们放在一起:
FUNCTION range_intersect(
p1 PERIOD_TABLE,
p2 PERIOD_TABLE
) RETURN PERIOD_TABLE IS
v_return PERIOD_TABLE;
v_len1 NUMBER(8);
v_len2 NUMBER(8);
BEGIN
v_len1 := p1.last;
v_len2 := p2.last;
WITH pa1 AS (
SELECT get_item(p1, column_value) AS period
FROM TABLE(generate_series(1, v_len1))
),
pa2 AS (
SELECT get_item(p2, column_value) AS period
FROM TABLE(generate_series(1, v_len2))
)
SELECT period(start_time, MIN(end_time))
BULK COLLECT INTO v_return
FROM (
SELECT (pa1.period).first() AS start_time
FROM pa1
WHERE contains(p1, (pa1.period).prev()) = 0
AND contains(p2, (pa1.period).first()) = 1
UNION ALL
SELECT (pa2.period).first() AS start_time
FROM pa2
WHERE contains(p1, (pa2.period).prev()) = 0
AND contains(p2, (pa2.period).first()) = 1
) s_start
... snip...
不容易,但可行。