Гость
Целевая тема:
Создать новую тему:
Автор:
Форумы / Oracle [игнор отключен] [закрыт для гостей] / Количество месяцев долга. Как получить одним запросом ? / 8 сообщений из 8, страница 1 из 1
27.01.2017, 07:17
    #39392642
lolo-soft
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Количество месяцев долга. Как получить одним запросом ?
Я только начал познавать PL/SQL в Oracle 11g.
Задачу решил через организацию цикла.

Возможно ли решение через запрос ? Предполагаю, что есть решение с помощью оконных функций, но пока не хватает понимания их работы.

Задача следующая:
1. Есть две таблицы: клиенты с их текущим балансом (customers) + суммы начислений за каждый месяц (calcs)

Код: plsql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
create table customers(
  cust_id number,
  balance number(15,2)  --- для должника balance < 0
);

create table calcs(
  month_calc date,
  cust_id number,
  calc_money number(15,2),  --- всегда положительное число
  pay_money number(15,2)   --- всегда положительное число
);



2. Нужно определить количество месяцев для условия: abs(минимальная СУММА по calc_money для cust_id) >= abs(balance для cust_id)

P.S. Мое решение: в цикле увеличивал отрицательный balance для cust_id на calc_money каждого месяца, начиная с текущего и "спускаясь" по месяцам в прошлое. Считал полученное кол-во месяцев до тех пор, пока balance >= 0 .
...
Рейтинг: 0 / 0
27.01.2017, 15:38
    #39393175
stax..
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Количество месяцев долга. Как получить одним запросом ?
lolo-soft,

приведите Ваше решение, будет более понятно что Вам надо

напр month_calc месяц или дата? уникально или нет?

и желательно тестовые данные

гляньте на sum( calc_money) over (partition by cust_id order by month_calc date DESC)


......
stax
...
Рейтинг: 0 / 0
31.01.2017, 06:26
    #39395085
lolo-soft
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Количество месяцев долга. Как получить одним запросом ?
stax , спасибо за ответ.

Тестовые данные:
Код: 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.
create global temporary table tmp_customer_debts(
    cust_id number,
    balance number (15,2) default 0,
    cnt_month smallint default 0,
    constraint PK_CUST_ID primary key (cust_id)
) on commit preserve rows;

insert into customers values(1,-200);
insert into customers values(2,-250);
insert into customers values(3,-300);
insert into customers values(4,-100);

declare
    i smallint:= 0;
    dd date:= to_date('2016.12', 'yyyy.mm'); 
    
begin
    loop
        insert into calcs values(add_months(dd,-i),1,50,0);
        insert into calcs values(add_months(dd,-i),2,50,0);
        insert into calcs values(add_months(dd,-i),3,50,0);
        insert into calcs values(add_months(dd,-i),4,50,0);
        
        i:= i + 1;
    exit when i > 10;
    end loop;
    
end;




Мое решение:
Код: 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.
declare
    s_month date := to_date('2016.12', 'yyyy.mm');
    i smallint := 0;
    c_balance number(15,2);
    c_money number(15,2); 
    
begin
    --очистить вр.таблицу
    execute immediate('truncate table tmp_customer_debts');
    
    --выбрать должников
    insert into tmp_customer_debts(cust_id, balance)
    select cust_id, balance
    from customers
    where balance <= -0.01;
        
    -- цикл по должникам
    for rec in ( select cust_id, balance from tmp_customer_debts ) loop
        i:= 0;
        
        c_balance:= rec.balance;
        c_money:= 0;
            
        loop
            select calc_money into c_money
            from calcs
            where month_calc = add_months(s_month, -i) and cust_id = rec.cust_id;
            
            --dbms_output.put_line('cust_id: '||rec.cust_id || '  c_money:' || c_money || '  c_balance:' || c_balance || '  i:' || i);
                
            c_balance:= c_balance + c_money;
            
            i:= i + 1;
        exit when (i = 10 or c_balance >= 0);
        end loop;
            
        update tmp_customer_debts
        set cnt_month = i
        where cust_id = rec.cust_id;        
    end loop;
end;    

select *
from tmp_customer_debts
...
Рейтинг: 0 / 0
31.01.2017, 16:45
    #39395667
fortnet
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Количество месяцев долга. Как получить одним запросом ?
stax..lolo-soft,

приведите Ваше решение, будет более понятно что Вам надо

напр month_calc месяц или дата? уникально или нет?

и желательно тестовые данные

гляньте на sum( calc_money) over (partition by cust_id order by month_calc date DESC)


......
stax

Ну и дальше просто:
select cust_id,balance,count(*)
from (
select c.balance, s.cust_id, sum( calc_money) over (partition by s.cust_id order by month_calc DESC) s_sum
from calcs s ,customers c where s.cust_id=c.cust_id)
where balance+s_sum <=0
group by balance,cust_id
order by 1
...
Рейтинг: 0 / 0
01.02.2017, 16:28
    #39396501
stax..
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Количество месяцев долга. Как получить одним запросом ?
lolo-soft,

получилось?

.....
stax
...
Рейтинг: 0 / 0
02.02.2017, 04:51
    #39396892
lolo-soft
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Количество месяцев долга. Как получить одним запросом ?
Спасибо ВСЕМ !!
Этот запрос работает. Основную задачу решает. Нужно будет учесть только один момент - если всех начислений не будет "хватать", чтобы "погасить" текущий баланс.

Решение от fortnet
Код: plsql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
select
    cust_id,
    balance,
    count(1) as cnt_month
from
    (
        select c.balance, s.cust_id, sum( calc_money) over (partition by s.cust_id order by month_calc DESC) as s_sum
        from
            sam_tmp_calcs s
            inner join sam_tmp_customers c on c.cust_id = s.cust_id
    )
where
    balance + s_sum <= 0 
group by
    cust_id, balance
order by 1
...
Рейтинг: 0 / 0
02.02.2017, 05:03
    #39396893
lolo-soft
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Количество месяцев долга. Как получить одним запросом ?
Правильно ли понимаю - как будет работать подзапрос " ...sum( calc_money) over (partition by s.cust_id order by month_calc DESC) as s_sum.... " - суммировать (группировать) значения calc_money для каждого месяца month_calc только из значений предыдущих месяцев, отсортированных по убыванию, для каждого cust_id ?
...
Рейтинг: 0 / 0
02.02.2017, 09:24
    #39396961
fortnet
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Количество месяцев долга. Как получить одним запросом ?
Да, при отсутствии задания параметров окна работает по умолчанию так (согласно заданной сортировке).
Это можно посмотреть :
select cust_id, calc_money, month_calc , sum( calc_money) over (partition by s.cust_id order by month_calc DESC) s_sum
from calcs s
order by cust_id,month_calc desc;
...
Рейтинг: 0 / 0
Форумы / Oracle [игнор отключен] [закрыт для гостей] / Количество месяцев долга. Как получить одним запросом ? / 8 сообщений из 8, страница 1 из 1
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


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