按 included/excluded/null 个日期范围限制记录

Limiting records by included/excluded/null date ranges

首先,描述一下我的任务。我需要确定过去 2 年内下过订单的客户。但是,我需要这些记录的一个子集。

  1. 需要在 12-24 个月前下过 1 个或多个订单。
  2. 1-12 个月前 没有 订单。
  3. 过去一个月内下了 1 个或多个新订单。

听起来很简单,但我花了太多时间来隔离约束而没有收到所需的输出。

这是我当前的代码尝试:

SELECT * FROM
(SELECT CUSTOMER_ID AS "CUSTOMER", NAME, DATE_ENTERED,
ROW_NUMBER() OVER(PARTITION BY CUSTOMER_ID
ORDER BY DATE_ENTERED desc) SEQ
FROM
A_ATEST
WHERE
DATE_ENTERED >= ADD_MONTHS(TRUNC(sysdate),-24) AND
(DATE_ENTERED >= ADD_MONTHS(TRUNC(sysdate),-1) AND
DATE_ENTERED < ADD_MONTHS(TRUNC(sysdate),-12)) AND
NOT EXISTS(SELECT null FROM A_ATEST WHERE
DATE_ENTERED < ADD_MONTHS(TRUNC(sysdate),-1) AND
DATE_ENTERED > ADD_MONTHS(TRUNC(sysdate),-12))
) a
  WHERE
  (SEQ = 1 AND
  DATE_ENTERED >= ADD_MONTHS(TRUNC(sysdate),-1)) AND
  (SEQ = 2 AND
  DATE_ENTERED < ADD_MONTHS(TRUNC(sysdate),-12))

示例数据:(我看不到添加 table 的方法,所以这里...)

CUSTOMER, NAME, DATE_ENTERED
100       A     08-APR-20
100       A     01-MAR-20
100       A     01-MAR-20
101       B     09-MAR-20
101       B     07-MAR-19
101       B     01-MAR-19
102       C     04-APR-20
102       C     03-JAN-19
102       C     05-JAN-18

理想情况下,我当前代码的结果集应显示:

CUSTOMER, NAME, DATE_ENTERED, SEQ
102       C     04-APR-20     1
102       C     03-JAN-19     2

我不喜欢我的代码。我希望有人能引导我找到更好的方法来完成这项任务。

谢谢!

-杜格伯特

您需要最近两年的订单,间隔一年。这表明 lag():

select a.*
from (select a.*,
             max(case when prev_de < add_months(date_entered, -12) then 1 else 0 end) over (partition by customer_id) as has_12month_gap
      from (select a.*,
                   lag(date_entered) over (partition by CUSTOMER_ID order by date_entered) as prev_de,
                   max(date_entered) over (partition by customer_id) as max_de
            from A_ATEST a
            where date_entered > add_months(sysdate, -24)
           ) a
     ) a
where max_de > add_months(sysdate, -1) and 
      has_12month_gap = 1;

编辑:

以上带回了所有交易。仅对客户而言,也是类似的逻辑,但更简单一些:

select customer
from (select a.*,
             lag(date_entered) over (partition by CUSTOMER_ID order by date_entered) as prev_de
      from A_ATEST a
      where date_entered > add_months(sysdate, -24)
     ) a
group by customer
where max(date_entered) > add_months(sysdate, -1) and 
      max(case when prev_de < add_months(date_entered, -12) then 1 else 0 end) = 1;

我想这会给你想要的。你的问题说你想要客户列表,但你的输出数据表明你想要这些客户的订单列表。

SELECT CUSTOMER_ID AS "CUSTOMER", NAME, DATE_ENTERED,
  FROM A_ATEST a1
 WHERE a1.DATE_ENTERED >= ADD_MONTHS(TRUNC(sysdate),-24)
   AND EXISTS ( SELECT 1 FROM A_ATEST a3
                 WHERE a3.customer_id  = a1.customer_id
                   AND a3.DATE_ENTERED BETWEEN ADD_MONTHS(TRUNC(sysdate), -24)
                                           AND ADD_MONTHS(TRUNC(sysdate), -12))
   AND NOT EXISTS ( SELECT 1 FROM A_ATEST a2 
                     WHERE a2.customer_id  = a1.customer_id
                       AND DATE_ENTERED < ADD_MONTHS(TRUNC(sysdate), -1) 
                       AND DATE_ENTERED > ADD_MONTHS(TRUNC(sysdate), -12))
   AND EXISTS ( SELECT 1 FROM A_ATEST a4
                 WHERE a4.customer_id  = a1.customer_id
                   AND a4.DATE_ENTERED > ADD_MONTHS(TRUNC(sysdate), -12))

这里的关键是您的子查询需要将 customer_id 关联回最外层的 A_ATEST table。你写的方式基本上意味着 "and there exists an order from any customer between 1 and 12 months ago".

如果以后有人提到我的问题,我想分享我最终的生产解决方案。因此,有许多更改以获得我需要的输出。

SELECT DISTINCT a1.CUSTOMER_NO AS "CUSTOMER", ci.NAME, MAX(a1.DATE_ENTERED) AS "ORDER DATE", a1.SALESMAN_CODE AS "SALESPERSON"
  FROM CUSTOMER_INFO ci LEFT JOIN CUSTOMER_ORDER a1 ON ci.CUSTOMER_ID = a1.CUSTOMER_NO
 WHERE a1.DATE_ENTERED >= ADD_MONTHS(TRUNC(sysdate), -1)
   AND EXISTS ( SELECT 1 FROM CUSTOMER_ORDER a3
                 WHERE a3.customer_no  = a1.customer_no
                   AND a3.DATE_ENTERED BETWEEN ADD_MONTHS(TRUNC(sysdate), -24)
                                           AND ADD_MONTHS(TRUNC(sysdate), -12))
   AND NOT EXISTS ( SELECT 1 FROM CUSTOMER_ORDER a2 
                     WHERE a2.customer_no  = a1.customer_no
                       AND DATE_ENTERED < ADD_MONTHS(TRUNC(sysdate), -1) 
                       AND DATE_ENTERED > ADD_MONTHS(TRUNC(sysdate), -12))
   AND EXISTS ( SELECT 1 FROM CUSTOMER_ORDER a4
                 WHERE a4.customer_no  = a1.customer_no
                   AND a4.DATE_ENTERED > ADD_MONTHS(TRUNC(sysdate), -24))
GROUP BY a1.CUSTOMER_NO, ci.NAME, a1.SALESMAN_CODE
ORDER BY a1.CUSTOMER_NO, "ORDER DATE"

再次感谢 eaolson 和 Gordon Linoff 帮助我到达我需要去的地方。