2017-01-23 1 views
3

у меня есть несколько счетовAccounts Негативные за 6 месяцев или более Независимо от сделок

Select * from Trans; 

AccountID PostDate Description Amount 
1   07/01/2016 deposit  10.00 
1   07/09/2016 withdrawal -15.00 

Второй счет:

AccountID PostDate Description Amount 
2   07/01/2016 deposit  10.00 
2   07/13/2016 withdrawal -20.00 
2   01/05/2017 deposit  8.00 

Третий счет:

AccountID PostDate Description Amount 
3   07/05/2016 deposit  10.00 
3   07/19/2016 deposit  20.00 
3   08/28/2016 withdrawal -45.00 

Четвертый Счет:

AccountID PostDate Description Amount 
4   01/05/2016 deposit  10.00 
4   01/19/2016 withdrawal -20.00 
4   09/28/2016 deposit  40.00 
4   10/01/2016 withdrawal -50 

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

Мне нужен общий запрос ... поскольку у меня больше, чем два указанных выше счета в таблице trans.

Запрос должен выбрать идентификатор учетной записи 1, поскольку он является отрицательным в течение более 180 дней. Он должен забрать AccountID 2, поскольку он отрицательный с 13.07.2012. В июле было отрицательным -10, и хотя депозит пришел, он по-прежнему оставался отрицательным -2 в январе 2017 года. Он не должен подбирать AccountID 3, потому что баланс отрицательный, но он остался отрицательным на 28.08.2016, и это означает, что это отрицательный только на 148 дней. Я также не хочу забирать счет «4». Хотя это было последовательно отрицательным в течение 6 месяцев или более, а также в настоящее время оно отрицательно .... но я хочу получать счета, чей текущий баланс отрицателен во все дни с 23.01.2017 по 07/23/2016.

Thanks

+0

Что означает «более 180 дней»? Вам нужно подобрать счет с отрицательным сальдо в течение 1,5 лет, с июля 2013 года по январь 2015 года? Или только учетные записи, у которых есть отрицательный * текущий * баланс (как 'SYSDATE'), и который имел отрицательный баланс на все времена за последние 180 дней? ** Также **: Какую версию Oracle вы используете? Различные версии имеют разные инструменты. (Если вы не знаете, запустите 'select * from v $ version'). – mathguy

+0

У вас есть опечатка на столе для учетной записи 3? Кажется, вы показываете отрицательные суммы для снятия средств и положительны для депозитов, но для Счета 3 у вас есть вывод 20,00 (положительный) и депозит в размере -45,00, что это значит? – mathguy

+0

есть. Любая учетная запись, которая является отрицательной в течение 6 месяцев или дольше, но в настоящее время она должна быть отрицательной. Например: учетная запись «55» отрицательна с июля 2013 года по январь 2017 года. Мне нужно выбрать ее. Но счет «66», который был отрицательным с июля 2013 года по декабрь 2016 года, но теперь он стал положительным в январе 2017 года. Я не хочу это выбирать. Я запускаю Oracle 12c. И я исправил опечатку на счет 3. Спасибо – Amir

ответ

1

Настройка Oracle:

CREATE TABLE trans (AccountID, PostDate, Description, Amount) AS 
SELECT 1, DATE '2016-07-01', 'deposit',  10.00 FROM DUAL UNION ALL 
SELECT 1, DATE '2016-07-09', 'withdrawal', -15.00 FROM DUAL UNION ALL 
SELECT 2, DATE '2016-07-01', 'deposit',  10.00 FROM DUAL UNION ALL 
SELECT 2, DATE '2016-07-13', 'withdrawal', -20.00 FROM DUAL UNION ALL 
SELECT 2, DATE '2017-01-05', 'deposit',  8.00 FROM DUAL UNION ALL 
SELECT 3, DATE '2016-07-05', 'deposit',  10.00 FROM DUAL UNION ALL 
SELECT 3, DATE '2016-07-19', 'deposit',  20.00 FROM DUAL UNION ALL 
SELECT 3, DATE '2016-08-28', 'withdrawal', -45.00 FROM DUAL UNION ALL 
SELECT 4, DATE '2016-01-05', 'deposit',  10.00 FROM DUAL UNION ALL 
SELECT 4, DATE '2016-01-19', 'withdrawal', -20.00 FROM DUAL UNION ALL 
SELECT 4, DATE '2016-09-28', 'deposit',  40.00 FROM DUAL UNION ALL 
SELECT 4, DATE '2016-10-01', 'withdrawal', -50.00 FROM DUAL; 

Запрос:

SELECT accountid 
FROM (
    SELECT t.*, 
     SUM(amount) OVER (PARTITION BY AccountID ORDER BY postdate) 
      AS balance 
    FROM trans t 
) 
GROUP BY accountid 
HAVING MAX(balance) KEEP (DENSE_RANK LAST ORDER BY postdate) < 0 
AND ( MAX(postdate) <= TRUNC(SYSDATE) - 180 
     OR MAX(CASE WHEN postdate >= TRUNC(SYSDATE) - 180 
        THEN balance - amount END) < 0 
     ); 

Выход:

ACCOUNTID 
---------- 
     1 
     2 
0

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

select t.*, 
     sum(amount) over (partition by accountid order by postdate) as balance 
from trans t; 

Затем вы хотите, чтобы 180 дней баланса были положительными или отрицательными. Это сложнее. Вот скотина метод силы:

with tb as (
     select t.*, 
      sum(amount) over (partition by accountid order by postdate) as balance 
     from trans t 
    ) 
select distinct accountid 
from tb 
where tb.balance < 0 and 
     not exists (select 1 
        from tb tb2 
        where tb2.accountid = tb.accountid and 
         tb2.balance > 0 and 
         tb2.postdate >= tb.postdate and tb2.postdate < tb.postdate + 180 
       ); 
0

Вот способ решить эту проблему с MATCH_RECOGNIZE. Предполагается, что PostDate может не иметь дубликатов для любой учетной записи. (Если на определенную дату будет проведено несколько транзакций, время транзакций будет контролировать то, как они обрабатываются.)

Ключ - это пункт PATTERN. Мы ищем последовательность транзакций (включая ВСЕ строки для Учетной записи, так как мы привязаны к ^ и $), где первое от 0 до (неизвестное) количество строк не ограничено (строки a), тогда мы ищем одну строку (b) с отрицательным сальдо и датой в течение 180 дней или более до trunc(SYSDATE), а все остальные строки должны иметь отрицательный баланс.

with 
    trans (AccountID, PostDate, Description, Amount) as ( 
     select 1, to_date('07/01/2016', 'mm/dd/yy'), 'deposit' , 10.00 from dual union all 
     select 1, to_date('07/09/2016', 'mm/dd/yy'), 'withdrawal', -15.00 from dual union all 
     select 2, to_date('07/01/2016', 'mm/dd/yy'), 'deposit' , 10.00 from dual union all 
     select 2, to_date('07/13/2016', 'mm/dd/yy'), 'withdrawal', -20.00 from dual union all 
     select 2, to_date('01/05/2017', 'mm/dd/yy'), 'deposit' , 8.00 from dual union all 
     select 3, to_date('07/05/2016', 'mm/dd/yy'), 'deposit' , 10.00 from dual union all 
     select 3, to_date('07/19/2016', 'mm/dd/yy'), 'deposit' , 20.00 from dual union all 
     select 3, to_date('08/28/2016', 'mm/dd/yy'), 'withdrawal', -45.00 from dual union all 
     select 4, to_date('01/05/2016', 'mm/dd/yy'), 'deposit' , 10.00 from dual union all 
     select 4, to_date('01/19/2016', 'mm/dd/yy'), 'withdrawal', -20.00 from dual union all 
     select 4, to_date('09/28/2016', 'mm/dd/yy'), 'deposit' , 40.00 from dual union all 
     select 4, to_date('10/01/2016', 'mm/dd/yy'), 'withdrawal', -50 from dual 
    ) 
-- End of test data; SQL query begins BELOW THIS LINE 
select AccountID 
from trans 
match_recognize(
    partition by AccountID 
    order  by PostDate 
    pattern (^ a* b c* $) 
    define b as b.PostDate <= trunc(sysdate) - 180 and sum(Amount) < 0, 
      c as          sum(Amount) < 0 
) 
; 

ACCOUNTID 
---------- 
     1 
     2 

2 rows selected. 
+0

Что произойдет, если учетная запись была открыта за последние 180 дней и была открыта с отрицательным балансом? – MT0

+0

@ mt0 - Я полагаю из утверждения OP требования о том, чтобы такая учетная запись не включалась. Независимо от того, он не включен в выход из этого запроса. – mathguy

+0

Извините, я просто понял, что 12c находится только в более низкой среде. Пойдет жить через месяц. Таким образом, в производственной базе используется 11G. – Amir

0

Вы также можете попробовать этот фрагмент. Это также будет достаточным для вашего требования. Надеюсь это поможет.

SELECT b.acc 
FROM 
    (SELECT row_number() over(partition BY a.acc order by dt DESC) nr, 
    SUM(a.amt) over(partition BY a.acc order by 1 DESC) sm, 
    row_number() over(partition BY a.acc,tp order by a.dt DESC) dt1, 
    a.* 
    FROM 
    (SELECT 1 acc, '2016-07-01' dt, 'deposit' tp, 10.00 amt FROM dual 
    UNION ALL 
    SELECT 1, '2016-07-09' dt, 'withdrawal' tp, -15.00 amt FROM dual 
    UNION ALL 
    SELECT 2, '2016-07-01' dt, 'deposit' tp, 10.00 amt FROM dual 
    UNION ALL 
    SELECT 2, '2016-07-13' dt, 'withdrawal' tp, -20.00 amt FROM dual 
    UNION ALL 
    SELECT 2, '2017-01-05' dt, 'deposit' tp, 8.00 amt FROM dual 
    UNION ALL 
    SELECT 3, '2016-07-05' dt, 'deposit' tp, 10.00 amt FROM dual 
    UNION ALL 
    SELECT 3, '2016-07-19' dt, 'deposit' tp, 20.00 amt FROM dual 
    UNION ALL 
    SELECT 3, '2016-08-28' dt, 'withdrawal' tp, -45.00 amt FROM dual 
    UNION ALL 
    SELECT 4, '2016-01-05' dt, 'deposit' tp, 10.00 amt FROM dual 
    UNION ALL 
    SELECT 4, '2016-01-19' dt, 'withdrawal' tp, -20.00 amt FROM dual 
    UNION ALL 
    SELECT 4, '2016-09-28' dt, 'deposit' tp, 40.00 amt FROM dual 
    UNION ALL 
    SELECT 4, '2016-10-01' dt, 'withdrawal' tp, -50.00 amt FROM dual 
    )a 
)b 
WHERE (b.nr          = 1 
OR b.dt1           =1) 
AND b.sm           < 0 
AND b.tp           <> 'deposit' 
AND (TRUNC(sysdate) - to_date(b.dt,'yyyy-mm-dd')) > 180;