2014-10-05 2 views
2

У меня есть требование, в котором мне нужно вывести группу результат на основе rowCount.Группа Результат на основе RowCount в Oracle

Вот результирующий набор, который я получаю от моего SQL:

ID  Date  Count 
1  10/01/2013 50 
1  10/02/2013 25 
1  10/03/2013 100 
1  10/04/2013 200 
1  10/05/2013 175 
1  10/06/2013  45 
2  10/01/2013  85 
2  10/02/2013 100 

Могу ли я иметь их в качестве

id  date Count 
    1  10/03/2013 175 
    1  10/04/2013 200 
    1  10/05/2013 175 
    1  10/06/2013  45 
    2  10/02/2013 185 

Мне нужно, чтобы уменьшить набор результатов, группируя их количество < = 200 ID: ID. Скажем, например, общая сумма 10/01, 10/02 и 10/03 составила 175, поэтому мне нужно сгруппировать их в один ряд. Где, добавляя значения fir 10/05 и 10/06, было бы> 200, поэтому оставьте их негруппированными.

Возможно ли в Oracle 11g решить эту проблему с использованием PLSQL или SQL-аналитических функций?

Новый запрос REsult Запрашивается Есть ли способ вернуть результат, имеющий дополнительную колонку? StartD колонок Для каждой строки он должен принять Предыдущую Дату окончания этого

ID  StartD  EndDate  Count 
1  10/01/2013 10/03/2013 175 
1  10/03/2013 10/04/2013 200 
1  10/04/2013 10/05/2013 250 
1  10/05/2013 10/06/2013 190 
1  10/06/2013 10/08/2013 45 
2  10/01/2013 10/01/2013 185 
+1

Почему вы хотите сохранить хранимую процедуру (или функцию)? –

+0

Не обязательно storeProc, если его выполнимость с использованием аналитической функции, как указывает Mat, тоже заметна. Поскольку im Используя oracle 11g, функция match_recognize кажется не поддерживаемой. – Ragav

+1

Вы специально попросили «в PL/SQL», что означает хранимую процедуру или функцию –

ответ

1

Для такой задачи, вы могли бы использовать a pipelined table function для генерации требуемого результата.

Существует небольшая «сантехника», так как требуется определить некоторые дополнительные типы, но сама по себе функция представляет собой простую петлю над курсором, накапливая значения и генерируя строку либо при изменении id, либо когда накопленный общий предел Лимит.

Вы можете реализовать это многими способами. Здесь, используя обычный старый цикл, вместо для в курсоре, я получаю что-то не что безвкусный:

CREATE OR REPLACE TYPE stuff_row AS OBJECT (
    id   int, 
    stamp  date, 
    last_stamp date, 
    num   int 
); 
CREATE OR REPLACE TYPE stuff_tbl AS TABLE OF stuff_row; 
CREATE OR REPLACE FUNCTION partition_by_200 
RETURN stuff_tbl PIPELINED 
AS 
    CURSOR data IS SELECT id, stamp, num FROM stuff ORDER BY id, stamp; 
    curr data%ROWTYPE; 
    acc stuff_row := stuff_row(NULL,NULL,NULL,NULL); 
BEGIN 
    OPEN data; 
    FETCH data INTO acc.id,acc.stamp,acc.num; 
    acc.last_stamp := acc.stamp; 


    IF data%FOUND THEN 
    LOOP 
    FETCH data INTO curr; 

    IF data%NOTFOUND OR curr.id <> acc.id OR acc.num+curr.num > 200 
    THEN 
     PIPE ROW(stuff_row(acc.id,acc.stamp,acc.last_stamp,acc.num)); 
     EXIT WHEN data%NOTFOUND; 

     -- reset the accumulator 
     acc := stuff_row(curr.id, curr.stamp, curr.stamp, curr.num); 
    ELSE 
     -- accumulate value 
     acc.num := acc.num + curr.num; 
     acc.last_stamp := curr.stamp; 
    END IF; 
    END LOOP; 
    END IF; 

    CLOSE data; 
END; 

Использование:

SELECT * FROM TABLE(partition_by_200()); 

Используя тот же данные испытаний как Мат в его own answer, это производит:

ID STAMP  LAST_STAMP NUM 
1 10/01/2013 10/03/2013 175 
1 10/04/2013 10/04/2013 200 
1 10/05/2013 10/05/2013 250 
1 10/06/2013 10/07/2013 190 
1 10/08/2013 10/08/2013 45 
2 10/01/2013 10/02/2013 185 
+0

Спасибо за ваше предложение; Есть ли способ, как я могу добавить новый столбец в результирующий набор, я обновил свой вопрос с помощью Обновленного результата. – Ragav

+0

@balaji Обычно [не рекомендуется изменять свой вопрос после получения ответов] (http://meta.stackoverflow.com/questions/254521/editing-question-for-new-answer?rq=1). При этом, поскольку изменение здесь довольно незначительное, я нашел время, чтобы обновить свой ответ. Я позволил вам сравнить обе версии, чтобы определить несколько отличий. –

3

Вы можете сделать это в 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()); 
+0

Thanks Mat. Это дает мне подробную информацию о том, как работает match_recognize. Оцените свои усилия. это не совместимо с Oracle 11g? – Ragav

+0

Нет, это только 12c. Я считаю, что вы можете делать все, что 'match_recognize' делает с« регулярными »функциями анализа и окон и подзапросами/CTE, но я не эксперт по SQL. Если я что-то придумаю, я обновлю сообщение. – Mat

2

Это возвращает ожидаемый результат, основанный на данных выборки. Я не уверен на 100%, хотя, если он будет работать во всех случаях (и это, вероятно, не будет очень эффективным):

with summed_values as (
    select stuff.*, 
     case 
      when sum(cnt) over (partition by id order by count_date) >= 200 then 1 
      else 0 
     end as sum_group 
    from stuff 
), totals as (
    select id, 
     max(count_date) as last_count, 
     sum(cnt) as total_count 
    from summed_values 
    where sum_group = 0 
    group by id 
    union all 
    select id, 
     count_date as last_count, 
     sum(cnt) as total_count 
    from summed_values 
    where sum_group = 1 
    group by id, count_date 
) 
select * 
from totals 
order by id, last_count 
; 

SQLFiddle пример: http://sqlfiddle.com/#!4/4e0d8/1

+0

Добавить «insert into stuff» (id, count_date, cnt) значения (1, дата «2013-09-30», 200); 'сверху, перестает работать, к сожалению. – Mat

Смежные вопросы