【问题标题】:How to validate if there was 12 sequential payments如何验证是否有 12 次连续付款
【发布时间】:2011-04-21 11:27:51
【问题描述】:

例如:

我有这种情况,我们收到付款,每个家庭的单笔付款,并在 DB 中注册这些付款。

问题是一个家庭可以将他们的贷款从 bank1 转移到 bank2,前提是他们有 12 次或更多连续付款。

例如,如果他们注册了付款

oct, nov, dec, jan, feb, mar, apr, may, jun, jul, ago, and sept.

feb 没有收到任何付款,计数将从march 重新开始。

同事们建议最好的方法是,在每次付款注册中计算总付款,并在名为 sequentialint 列中注册总连续付款。

作为:

Payment     Family      Bank     Date          Sequential
---------------------------------------------------------
1200        2           1        10-22-2009    1
1200        2           1        11-22-2009    2
.
.
.
1200        2          1        08-22-2010     11
1200        2          1        09-22-2010     12

我认为,必须有一种方法,其中 sequential 列是不必要的,如果我想验证最后的 order by Date DESC 12 行是否是连续的,只有 1 月的差异。

有什么想法吗?

已编辑:

  • 这个table会有百万个rows

  • 还希望只在表格中包含日期,并通过application level 处理它们

【问题讨论】:

    标签: sql database oracle database-design optimization


    【解决方案1】:

    我认为一个简单的查询会有所帮助,请检查:

    SELECT COUNT(*)
    FROM payments p
    WHERE p.Family = 2 AND p.Date between '01-01-2009' and '12-01-2009'
    

    这样,您将获得当前表格结构中任意日期之间的付款次数。

    【讨论】:

    • 不回答 OP 的问题 - 3 月份的两次付款和 6 月份的一次付款仍然返回 12。
    • 此外,强烈建议不要在任何日期比较 SQL 中使用字符串文字,因为日期格式是 NLS 和特定于会话的
    • 我真的很喜欢这个简单的答案,因为(此时)我的系统中不可能有两次付款。如果您两次支付该贷款(另一个表),我们有一个流程来汇总这些付款并将它们作为一次付款插入payment 表中。我认为,在这个例子中,我应该将付款称为“发票”。
    • @adam,我忽略了那个场景,因为 garis 没有指定它。
    • @andr,你愿意解释一下吗?我不知道这个话题
    【解决方案2】:

    这个怎么样:

    SELECT  PT.Payment
          , PT.Family
          , PT.Bank
          , PT.Date
          , (SELECT COUNT(*) FROM PaymentTable T
             WHERE DATEDIFF (d, T.Date, PT.Date) < 31) as IsSequential
      FROM PaymentsTable PT
    

    上面的查询会告诉您每笔付款是否是连续的(即是否在前一个月进行了付款)

    然后您可以运行查询以确定是否有 12 次连续付款是针对特定月份或特定家庭进行的。

    假设您要显示至少有 12 次连续付款的所有家庭:

    SELECT  ST.Family
         , COUNT(ST.IsSequential) as NumberOfSequentialPayments
     FROM
      (SELECT   PT.Payment
              , PT.Family
              , PT.Bank
              , PT.Date
              , (SELECT COUNT(*) FROM PaymentTable T
              WHERE DATEDIFF (d, T.Date, PT.Date) < 31) as IsSequential
         FROM PaymentsTable PT
       ) AS ST
      WHERE NumberOfSequentialPayments >= 12
     GROUP BY ST.Family
    

    【讨论】:

    • 这些 query 将在百万 (100,000,000) 行表中进入 ok
    • 这真的取决于很多条件,例如索引、服务器资源——也许最重要的是,用户期望;)
    • 二月有 28 天。因此,01-31 的付款和 03-01 的后续付款将被错误地认定为连续几个月。
    • 好吧@Adam Musch,如果你打算走那条路,大多数银行都会给予 x 天的宽限期,因此可能会在同一个月进行双倍付款,适用于上个月以及双倍付款月份.. 所以查询的一部分应该被替换为更强大的东西
    【解决方案3】:

    分析!

    数据:

    create table payments
    (amount       number,
     family       number,
     bank         number,
     payment_date date
    );
    
    insert into payments values (1200, 2, 1, date '2010-01-01');
    insert into payments values (1200, 2, 1, date '2010-02-02');
    insert into payments values (1200, 2, 1, date '2010-03-03');
    insert into payments values (1200, 2, 1, date '2010-04-04');
    insert into payments values (1200, 2, 1, date '2010-05-05');
    insert into payments values (1200, 2, 1, date '2010-06-07');
    insert into payments values (1200, 2, 1, date '2010-07-07');
    --skip august
    --insert into payments values (1200, 2, 1, date '2010-08-08');
    insert into payments values (1200, 2, 1, date '2010-09-09');
    insert into payments values (1200, 2, 1, date '2010-10-10');
    insert into payments values (1200, 2, 1, date '2010-11-11');
    --double pay november
    insert into payments values (1200, 2, 1, date '2010-11-30');
    insert into payments values (1200, 2, 1, date '2010-12-12');
    

    查询:

    select * 
      from (select family, bank, 
                   trunc(payment_date, 'mon') as payment_month,
                   lead ( trunc(payment_date, 'mon')) 
                     over ( partition by family
                            order by payment_date) 
                     as next_payment_month
              from payments 
             order by payment_date desc 
           )
           -- eliminate multiple payments in month
     where payment_month <> next_payment_month
           -- find a gap
       and add_months(payment_month, 1) <> (next_payment_month)
           -- stop at the first gap
       and rownum = 1
    

    结果:

        FAMILY       BANK PAYMENT_M NEXT_PAYM
    ---------- ---------- --------- ---------
             2          1 01-JUL-10 01-SEP-10
    

    您可以使用NEXT_PAYMENT_MONTH 中的值在应用程序级别执行您想要的任何比较。


    SELECT trunc(MONTHS_BETWEEN(SYSDATE, DATE '2010-01-01')) FROM DUAL
    

    给你几个月的时间——这就是我在应用程序级别使用值的意思。

    所以这个:

    select trunc(
           months_between(sysdate, 
             (select next_payment_date
                from (select family, bank, 
                             trunc(payment_date, 'mon') as payment_month,
                        lead ( trunc(payment_date, 'mon')) 
                        over ( partition by family
                               order by payment_date) 
                          as next_payment_month
                        from payments 
                       where family = :family
                       order by payment_date desc 
                     )
               where payment_month <> next_payment_month
                 and add_months(payment_month, 1) <> (next_payment_month)
                 and rownum = 1
              ) 
           )
      from dual
    

    为您提供自上次错过的月份以来连续付款的月份数。

    【讨论】:

    • 另外,我不知道我是否错了,但我看不出它在哪里可以帮助我解决我的问题。我的意思是,有了这个,我会得到中断的月份。而我正在寻找的就像是计算当前的连续付款。举个例子,说明连续付款的计数是4,因为付款月份是septoctnov和`dec.
    【解决方案4】:

    正如其他人指出的那样,这是可能的。

    但是,当您拥有关系数据时,情况并非如此,而是您按顺序执行操作,这是坏事

    这是一种业务规则本质上是顺序的情况;在这种情况下,具有顺序辅助字段可能

    • 简化您的查询
    • 提高性能(如果您谈论 100M 记录,这突然成为几乎最高评价的因素,并且各种非规范化想法浮现在脑海中)
    • 对其他业务规则有意义(允许更多功能和灵活性)

    关于最后一点:我认为最完整的解决方案需要重新检查业务规则 - 您可能会发现用户会谈论“未付款”,这会建议其他表格,例如“付款计划/时间表”和与其他流程相关联,这可能是错过付款列或顺序值的正确位置...这种结构还支持宽限期、预付款等方面的灵活性...

    【讨论】:

    • Oracle 和其他 RDBMS 中包含的分析功能为 RDBMS 通常非常薄弱的​​问题(例如行之间的比较操作)添加了有效的解决方案。考虑到 payment_date 列很好地服务于该功能,我看不出顺序帮助字段如何增加任何值。
    • @Adam,实际上您是对的,您的回答非常好(+1)。我在为我的武器库添加分析功能方面一直很慢。因为我不想抹去答案,所以我会试着找点时间做一些基准测试……然后更新我的答案。
    【解决方案5】:

    要验证一个家庭在过去 12 个月中是否有 12 次连续付款(不考虑银行),请使用:

    select sum(payment)                          total_paid, 
           count(*)                              total_payments, 
           count(distinct trunc(pay_date,'mon')) paid_months
    from   payment_table
    where  family = :family and pay_date between :start_date and :end_date;
    

    total_payments 表示该期间的付款次数,而paid_months 表示付款的单独月份数。

    如果您想检查他们是否在选定的时间段内已经切换银行,请在上述查询中添加group by bank 子句。

    要列出该期间内有 12 个不同月份付款的所有家庭,请使用:

    select family,
           sum(payment)                          total_paid, 
           count(*)                              total_payments, 
           count(distinct trunc(pay_date,'mon')) paid_months
    from   payment_table
    where  pay_date between :start_date and :end_date
    group by family
    having count(distinct trunc(pay_date,'mon')) = 12;
    

    如果您想将结果限制在选定期间尚未切换银行的家庭,请在上述查询的having 子句中添加and count(distinct bank) = 1 条件。

    我建议确保付款表有关于家庭和 pay_date 的索引。

    【讨论】:

    • 有用的答案。此外,无论银行如何,付款都是顺序的。谢谢
    猜你喜欢
    • 2019-01-15
    • 1970-01-01
    • 2014-01-14
    • 1970-01-01
    • 2021-05-15
    • 2017-11-18
    • 2012-04-10
    • 1970-01-01
    • 2021-11-13
    相关资源
    最近更新 更多