【问题标题】:How can I return rows for IDs that meet criteria for EXACTLY 3 occurrences in a row?如何返回符合条件的 ID 行恰好连续出现 3 次?
【发布时间】:2017-11-17 14:12:03
【问题描述】:

我有一个要求让我陷入困境。我必须返回具有准确连续 3 行每月发票金额 > 2,000 美元的位置 ID。换句话说,我不想返回成熟位置的 ID(可能有数百个月度发票行)。

Rextester 示例数据:http://rextester.com/CNJC15871

信息:

  • 报告将在每月的第一天生成
  • 每月发票日期为每月 15 日

期望的输出:在下面的发票表中,

  • 对于 10 月 1 日的报表运行日期,将返回 loc_ids 2223 和 3344,因为 9/15 是发票金额连续第三个月 >$2,000
  • 对于 11 月 1 日的报表运行日期,将按照相同的逻辑返回 loc_id 6678。
  • 对于随后的报告月份,应返回 2223、3344 和 6678,因为它们连续 >3 个月 >$2,000。

    | loc_id | invoice_date | invoice_amt | Notes                     |
    |--------|--------------|-------------|---------------------------|
    | 1234   | 5/15/2002    | 7000        |                           |
    | 1234   | 6/15/2002    | 8000        |                           |
    | ..     | …            | …           |                           |
    | 1234   | 11/15/2017   | 58000       |                           |
    |        |              |             |                           |
    | 9987   | 11/15/2006   | 7500        |                           |
    | 9987   | 12/15/2006   | 8500        |                           |
    | …      | …            |             |                           |
    | 9987   | 11/15/2017   | 63000       |                           |
    |        |              |             |                           |
    | 5544   | 3/15/2015    | 9200        |                           |
    | 5544   | 4/15/2015    | 10000       |                           |
    | …      | …            |             |                           |
    | 5544   | 11/15/2017   | 70000       |                           |
    |        |              |             |                           |
    | 2223   | 5/15/2017    | 2500        | Count| >2000              |
    | 2223   | 6/15/2017    | 1375        | Do not count| <2000       |
    | 2223   | 7/15/2017    | 8000        | Restart count| >2000 (1)  |
    | 2223   | 8/15/2017    | 9000        | Continue count| >2000 (2) |
    | 2223   | 9/15/2017    | 9800        | Continue count| >2000 (3) |
    | 2223   | 10/15/2017   | 10500       | Stop count| >3 in a row   |
    | 2223   | 11/15/2017   | 11200       | Stop count| >3 in a row   |
    |        |              |             |                           |
    | 3344   | 7/15/2017    | 3500        | Count| >2000 (1)          |
    | 3344   | 8/15/2017    | 4500        | Continue count| >2000 (2) |
    | 3344   | 9/15/2017    | 6000        | Continue count| >2000 (3) |
    | 3344   | 10/15/2017   | 7000        | Stop count| >3 in a row   |
    | 3344   | 11/15/2017   | 8000        | Stop count| >3 in a row   |
    |        |              |             |                           |
    | 6678   | 8/15/2017    | 3000        | Count| >2000 (1)          |
    | 6678   | 9/15/2017    | 4000        | Continue count| >2000 (2) |
    | 6678   | 10/15/2017   | 5000        | Continue count| >2000 (3) |
    

我还有一个包含营业地点开放日期的 loc_id 维度。

| loc_id | loc_open_dt |
|--------|-------------|
| 1234   | 2002-05-01  |
| 9987   | 2006-10-22  |
| 5544   | 2015-03-04  |
| 2223   | 2017-05-05  |
| 3344   | 2017-07-05  |
| 6678   | 2017-08-01  | 

【问题讨论】:

  • 如果在经过较长时间的 +2000 数量之后,它们再次更低,并且出现了一个新的 +2000 的 3 系列怎么办。那么该位置是否应该再次列出?如果某个位置恰好有 3 个连续的 +2000 金额,但之后金额又较低,是否应该在这些较低的月份继续列出?
  • 第一个问题:是 - 如果在运行 3 次且 >2000 后 ID 跌至 2000 以下,则应再次列出该 ID。第二个问题:是的 - 如果一个 ID 正好有 3 个 >2000,那么在第 4 个月下降到
  • 使用 group by 聚合表达式:https://www.postgresql.org/docs/9.4/static/sql-expressions.html#SYNTAX-AGGREGATES
  • @JustMe URL 不存在

标签: sql postgresql


【解决方案1】:

这是检查以下内容的查询:

  • 金额 >=2000 的月份是连续的,并且
  • 上市日期前一个月是最后一个月,并且
  • 如果这三个前一个月有金额,则低于2000

查询:

select distinct loc_id
from   (
        select loc_id, 
               first_value(invoice_amt) over win                            first_amt,
               floor((list_date - first_value(invoice_date) over win)/30)+1 month_count,
               list_date - last_value(invoice_date) over win < 30           has_last_month,
               count(case when invoice_amt >= 2000 then 1 end) over win     large_amt_count
        from   invoices,
               (select date '2017-10-01' /* current_date */ list_date) ref 
        where  invoice_date between (list_date - 120) and list_date
        window win as (partition by loc_id order by invoice_date)
       ) base
where  month_count = 3 + (first_amt < 2000)::int
   and large_amt_count = 3
   and has_last_month;

看到它在rextester上运行

将查询中间的文字日期更改为您的实际报告日期(或current_date)。

【讨论】:

    【解决方案2】:

    在 PostgreSQL 中,您可以使用窗口函数。您需要通过 loc_id 聚合数据来构建窗口,然后检查 3 个连续行的 invoice_amt 是否大于目标值。这个技巧是通过使用lag() 函数来完成的,该函数应用于一个窗口,让您从previous 行中获取数据。代码比解释简单多了:

    SELECT DISTINCT loc_id FROM (
      SELECT *, 
             invoice_amt > 10000 AS a, 
             lag(invoice_amt, 1) OVER w > 10000 AS b,
             lag(invoice_amt, 2) OVER w > 10000 AS c,
             extract('month' from invoice_date::date) AS m1, 
             extract('month' from (lag(invoice_date, 1) OVER w)::date + '1 month'::interval) AS m2, 
             extract('month' from (lag(invoice_date, 2) OVER w)::date + '2 month'::interval) AS m3
        FROM invoices 
      WINDOW w AS (PARTITION BY loc_id ORDER BY invoice_date)
    ) X 
     WHERE a AND b AND c AND m2 = m1 AND m3 = m1
    

    还要注意连续个月的检查。我们只需将 1 或 2 个月添加到 lag()ged 日期,然后检查连续三行的月份是否相同(如 cmets 中所要求的)。

    如果您想更好地了解其工作原理,只需运行内部 SELECT 并查看结果。

    【讨论】:

    • 如何在其中包含功能/逻辑,以便它仅返回与报告运行日期相关的连续三个月的 ID?例如,我的样本数据中的 ID 1234 使用此方法将是误报,因为它连续第三个月是在 2002 年 8 月。
    • 您可以使用另一个lag() 调用从invoice_date 中提取月份。现在我不在电脑旁,我稍后会更新答案。
    • @psrpsrpst 我已经添加了关于连续几个月的检查。
    猜你喜欢
    • 2019-06-05
    • 2022-01-01
    • 1970-01-01
    • 2022-01-01
    • 1970-01-01
    • 2014-11-25
    • 1970-01-01
    • 2021-12-12
    • 1970-01-01
    相关资源
    最近更新 更多