Итерация коллекций объектов Oracle без их взрыва - PullRequest
2 голосов
/ 26 марта 2010

Я использую типы данных объекта 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, который не уничтожает мои объекты?

Ответы [ 2 ]

0 голосов
/ 29 марта 2010

Извините, это был действительно сложный вопрос. Но я наконец нашел способ сделать это, используя некоторые инструменты, которые я создал ранее. Хитрость заключалась в том, чтобы перебирать вложенную таблицу чисел, чтобы получить каждый элемент.

Итак, первая часть была серийным генератором, который я беззастенчиво позаимствовал у Постгреса. Есть и другие способы генерации чисел, но это довольно эффективно.

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...

Не просто, но выполнимо.

0 голосов
/ 29 марта 2010

Если я правильно понимаю ваш вопрос, проблема заключается в поведении оператора 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

При такой настройке я могу запросить таблицу period_table_tmp в SQL, как если бы она была коллекцией объекта 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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...