Вы можете сделать это в Oracle 12c с сопрягая технику в MATCH_RECOGNIZE
узоре.
Setup (добавлено несколько строк, в том числе некоторые с числом выше 200, для тестирования):
create table stuff (id int, stamp date, num int);
insert into stuff values (1, to_date('10/01/2013', 'MM/DD/RRRR'), 50);
insert into stuff values (1, to_date('10/02/2013', 'MM/DD/RRRR'), 25);
insert into stuff values (1, to_date('10/03/2013', 'MM/DD/RRRR'), 100);
insert into stuff values (1, to_date('10/04/2013', 'MM/DD/RRRR'), 200);
insert into stuff values (1, to_date('10/05/2013', 'MM/DD/RRRR'), 250);
insert into stuff values (1, to_date('10/06/2013', 'MM/DD/RRRR'), 175);
insert into stuff values (1, to_date('10/07/2013', 'MM/DD/RRRR'), 15);
insert into stuff values (1, to_date('10/08/2013', 'MM/DD/RRRR'), 45);
insert into stuff values (2, to_date('10/01/2013', 'MM/DD/RRRR'), 85);
insert into stuff values (2, to_date('10/02/2013', 'MM/DD/RRRR'), 100);
commit;
Запрос будет:
select id, first_stamp, last_stamp, partial_sum
from stuff
match_recognize (
partition by id order by stamp
measures
first(a.stamp) as first_stamp
, last(a.stamp) as last_stamp
, sum(a.num) as partial_sum
pattern (A+)
define A as (sum(a.num) <= 200 or (count(*) = 1 and a.num > 200))
);
Что дает:
ID FIRST_STAMP LAST_STAMP PARTIAL_SUM
---------- ----------- ---------- -----------
1 01-OCT-13 03-OCT-13 175
1 04-OCT-13 04-OCT-13 200
1 05-OCT-13 05-OCT-13 250
1 06-OCT-13 07-OCT-13 190
1 08-OCT-13 08-OCT-13 45
2 01-OCT-13 02-OCT-13 185
6 rows selected
Как это работает:
- Соответствие шаблону выполняется по всей таблице, разбитое на
id
и упорядоченное по метке времени.
- Образец
A+
говорит, что мы хотим, чтобы группы последовательных (по разделам и порядку по предложениям) строк удовлетворяли условию A
.
- Условие
A
является то, что множество удовлетворяет:
- Сумма NUM в наборе 200 или менее
- Или набор одну строку с NUM больше, чем 200 (в противном случае эти строки никогда не соответствуют друг другу, и Арен вывода).
- Предложение
measures
указывает на то, что возвращает матч (на верхней части ключа секционирования):
- первый и последний временные метки из каждой группы
- сумма NUM для каждой группы
Вот подход с функцией табличной, который должен работать в 11g (и 10ге я думаю). Скорее неэлегантный, но выполняет эту работу. Трассирует таблицу в порядке, выводя группы, когда они «полные».
Вы также можете добавить параметр для размера группы.
create or replace
type my_row is object (id int, stamp date, num int);
create or replace
type my_tab as table of my_row;
create or replace
function custom_stuff_groups
return my_tab pipelined
as
cur_sum number;
cur_id number;
cur_dt date;
begin
cur_sum := null;
cur_id := null;
cur_dt := null;
for x in (select id, stamp, num from stuff order by id, stamp)
loop
if (cur_sum is null) then
-- very first row
cur_id := x.id;
cur_sum := x.num;
elsif (cur_id != x.id) then
-- changed ID, so output last line for previous id and reset
pipe row(my_row(cur_id, cur_dt, cur_sum));
cur_id := x.id;
cur_sum := x.num;
elsif (cur_sum + x.num > 200) then
-- same id, sum overflows.
pipe row(my_row(cur_id, cur_dt, cur_sum));
cur_sum := x.num;
else
-- same id, sum still below 200
cur_sum := cur_sum + x.num;
end if;
cur_dt := x.stamp;
end loop;
if (cur_sum is not null) then
-- output the last line, if any
pipe row(my_row(cur_id, cur_dt, cur_sum));
end if;
end;
Использование в качестве:
select * from table(custom_stuff_groups());
Почему вы хотите сохранить хранимую процедуру (или функцию)? –
Не обязательно storeProc, если его выполнимость с использованием аналитической функции, как указывает Mat, тоже заметна. Поскольку im Используя oracle 11g, функция match_recognize кажется не поддерживаемой. – Ragav
Вы специально попросили «в PL/SQL», что означает хранимую процедуру или функцию –