powered by simpleCommunicator - 2.0.51     © 2025 Programmizd 02
Форумы / Oracle [игнор отключен] [закрыт для гостей] / Как сделать функцию pipelined?
3 сообщений из 3, страница 1 из 1
Как сделать функцию pipelined?
    #40029558
Alibek B
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
У меня есть функция с двумя вариантами:
Код: plsql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
FUNCTION PERIOD_NEXT(p_period NUMBER, p_date DATE := trunc(sysdate), p_duration NUMBER := 1) RETURN DATE IS
  v_res DATE;
  v_period BM_CNT_PERIOD%ROWTYPE;
BEGIN
  BEGIN
    select * into v_period from BM_CNT_PERIOD where CNT_PERIOD_ID = p_period;
  EXCEPTION WHEN NO_DATA_FOUND THEN
    RETURN NULL;
  END;
  v_res := trunc(p_date);
  IF (v_period.ADJUST IS NULL) THEN v_period.ADJUST := 0; END IF;
  IF (v_period.ADJUST > 0) THEN
    v_res :=
    CASE v_period.UNIT
      WHEN 'D' THEN v_res + p_duration*v_period.VALUE
      WHEN 'W' THEN trunc(v_res,'IW') + 7*p_duration*v_period.VALUE + v_period.ADJUST - 1
      WHEN 'M' THEN ADD_MONTHS(trunc(v_res,'MM'), p_duration*v_period.VALUE) + v_period.ADJUST - 1
      WHEN 'Y' THEN ADD_MONTHS(trunc(v_res,'YYYY'), 12*p_duration*v_period.VALUE) + v_period.ADJUST - 1
    END;
  ELSE
    v_res :=
    CASE v_period.UNIT
      WHEN 'D' THEN v_res + p_duration*v_period.VALUE
      WHEN 'W' THEN v_res + 7*p_duration*v_period.VALUE
      WHEN 'M' THEN ADD_MONTHS(v_res, p_duration*v_period.VALUE)
      WHEN 'Y' THEN ADD_MONTHS(v_res, 12*p_duration*v_period.VALUE)
    END;
  END IF;
  RETURN v_res;
END;
FUNCTION PERIOD_NEXT(p_unit BM_CNT_PERIOD.UNIT%TYPE, p_value BM_CNT_PERIOD.VALUE%TYPE, p_adjust BM_CNT_PERIOD.ADJUST%TYPE, p_date DATE := trunc(sysdate), p_duration NUMBER := 1) RETURN DATE IS
  v_res DATE;
BEGIN
  v_res := trunc(p_date);
  IF ((p_adjust IS NULL) OR (p_adjust = 0)) THEN
    v_res :=
    CASE p_unit
      WHEN 'D' THEN v_res + p_duration*p_value
      WHEN 'W' THEN v_res + 7*p_duration*p_value
      WHEN 'M' THEN ADD_MONTHS(v_res, p_duration*p_value)
      WHEN 'Y' THEN ADD_MONTHS(v_res, 12*p_duration*p_value)
    END;
  ELSE
    v_res :=
    CASE p_unit
      WHEN 'D' THEN v_res + p_duration*p_value
      WHEN 'W' THEN trunc(v_res,'IW') + 7*p_duration*p_value + p_adjust - 1
      WHEN 'M' THEN ADD_MONTHS(trunc(v_res,'MM'), p_duration*p_value) + p_adjust - 1
      WHEN 'Y' THEN ADD_MONTHS(trunc(v_res,'YYYY'), 12*p_duration*p_value) + p_adjust - 1
    END;
  END IF;
  RETURN v_res;
END;



Используются они примерно так:
Код: plsql
1.
2.
3.
4.
5.
select T.TARIFF_ID, T.TARIFF
--, PERIOD_NEXT(T.PERIOD, sysdate, nvl(T.DURATION,1)) as N1
--, PERIOD_NEXT(P.UNIT, P.VALUE, P.ADJUST, sysdate, nvl(T.DURATION,1)) as N2
from V_TARIFF T
join BM_CNT_PERIOD P on (P.CNT_PERIOD_ID = T.FEE_PERIOD)



В представлении V_TARIFF около сотни записей. Таблица BM_CNT_PERIOD небольшая, там пара десятков записей.
Изначально был только первый вариант (не мой) и он вообще использовался только для индивидуальной (построчной) обработки.
Запрос с таким вариантом (при раскомментированном N1) выполняется 15-20 секунд, скорее всего из-за select into, исключений и переключений контекста. К тому же функция вычисляется при каждом использовании, то есть если ее перечислить в select 10 раз, то и время выполнения увеличится в 10 раз.
Второй вариант написан мной, он выполняется почти мгновенно (на сотне записей не видно разницы с закомментированным и раскомментированным N2). Но как я понимаю, тут все равно смесь SQL и PL/SQL переключение контекста.

Теперь я бы хотел сделать третий вариант, в который бы я передавал только p_period, но чтобы он при этом работал быстро. Если я правильно понял, то для этого мне нужна pipelined, в которую сразу передается массив входных данных, а она выдает массив выходных данных, и при этом инициализация будет выполнятся только однажды.
Правильно ли я понял?
...
Рейтинг: 0 / 0
Как сделать функцию pipelined?
    #40029581
mcureenab
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Думаю, нет смысла заморачиваться ещё одним вариантом.

Конвейер в работе примерно так будет выглядеть.

Код: plsql
1.
2.
3.
select * from PERIOD_NEXT(select *
from V_TARIFF T
join BM_CNT_PERIOD P on (P.CNT_PERIOD_ID = T.FEE_PERIOD))



Скорее всего без разницы в производительности и область применения будет ограничена, потому как PERIOD_NEXT теперь должна возвращать не число, а всю запись.

Внутри PERIOD_NEXT в цикле fetch из ref cursor на входе и pipe записи на выход.
...
Рейтинг: 0 / 0
Как сделать функцию pipelined?
    #40029617
Alibek B
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
mcureenab
Конвейер в работе примерно так будет выглядеть.

Неожиданно. Я думал, что она используется как обычная функция, просто реализована с учетом того, чтобы работать в контексте SQL и соответственно будет более эффективной.
...
Рейтинг: 0 / 0
3 сообщений из 3, страница 1 из 1
Форумы / Oracle [игнор отключен] [закрыт для гостей] / Как сделать функцию pipelined?
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]