Postgresql 按顺序计算满足组内条件的连续条目数

Postgresql 按顺序计算满足组内条件的连续条目数,postgresql,Postgresql,我有一个拖欠账单的用户列表,我想为每个用户生成一个条目,说明他们连续拖欠了多少账单。下面是表格: user | bill_date | outstanding_balance --------------------------------------- a | 2017-03-01 | 90 a | 2016-12-01 | 60 a | 2016-09-01 | 30 b | 2017-03-01 | 50

我有一个拖欠账单的用户列表,我想为每个用户生成一个条目,说明他们连续拖欠了多少账单。下面是表格:

user |  bill_date | outstanding_balance
---------------------------------------
 a   | 2017-03-01 |        90
 a   | 2016-12-01 |        60
 a   | 2016-09-01 |        30
 b   | 2017-03-01 |        50
 b   | 2016-12-01 |        0
 b   | 2016-09-01 |        40
 c   | 2017-03-01 |        0
 c   | 2016-12-01 |        0
 c   | 2016-09-01 |        1
我想要一个生成下表的查询:

user | consecutive_billing_periods_behind
-----------------------------------------
  a  |               3
  b  |               1
  a  |               0

换句话说,如果你在任何时候都已经付清了,我想忽略前面的所有条目,只计算一下自从你上次付清账单以来你已经落后了多少个账单周期。如何最简单地做到这一点?

如果我正确理解了这个问题,首先您需要找到任何给定客户支付账单的最后日期,以便其未付余额的最后日期为0。可以通过此子查询执行此操作:

            (SELECT
              user1,
              bill_date AS no_outstanding_bill_date
            FROM table1
            WHERE outstanding_balance = 0)
然后,您需要获取最后的账单日期,并为每行创建字段(如果它们是未付账单)。然后根据where子句过滤每个客户的最后一个清零日到最后一个账单日期之间的行:

WHERE bill_date >= last_clear_day AND bill_date <= last_bill_date
然后,如果将这些部分放在一起,则可以通过此查询得到结果:

SELECT
  DISTINCT
  user1,
  sum(is_outstanding_bill)
  OVER (
    PARTITION BY user1 ) AS consecutive_billing_periods_behind
FROM (
       SELECT
         user1,
         last_value(bill_date)
         OVER (
           PARTITION BY user1
           ORDER BY bill_date
           ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING ) AS last_bill_date,
         CASE WHEN outstanding_balance > 0
           THEN 1
         ELSE 0 END                                                   AS is_outstanding_bill,
         bill_date,
         outstanding_balance,
         nvl(max(t2.no_outstanding_bill_date)
             OVER (
               PARTITION BY user1 ), min(bill_date)
             OVER (
               PARTITION BY user1 ))                                  AS last_clear_day

       FROM table1 t1
         LEFT JOIN (SELECT
                      user1,
                      bill_date AS no_outstanding_bill_date
                    FROM table1
                    WHERE outstanding_balance = 0) t2 USING (user1)
     ) table2
WHERE bill_date >= last_clear_day AND bill_date <= last_bill_date
因为我们使用的是distinct,所以您不需要GROUPBY子句

select
  user,
  count(case when min_balance > 0 then 1 end)
    as consecutive_billing_periods_behind
from
(
  select
    user,
    min(outstanding_balance)
      over (partition by user order by bill_date) as min_balance
  from tbl
)
group by user
或:

select
  user,
  count(*)
    as consecutive_billing_periods_behind
from
(
  select
    user,
    bill_date,
    max(case when outstanding_balance = 0 then bill_date) over
      (partition by user)
      as max_bill_date_with_zero_balance
  from tbl
)
where
  -- If user has no outstanding_balance = 0, then
  max_bill_date_with_zero_balance is null
  -- Count all rows in this case.

  -- Otherwise
  or
  -- count rows with
  bill_date > max_bill_date_with_zero_balance
group by user