英文:
Get customers that have ordered consistently each month - Oracle SQL
问题
我正在尝试提取在给定期间内每个月都下订单的客户。我试图包括所有客户,直到他们停止下订单。连续
我已经在下面的表格中创建了我要提取的结果。
客户 | 日期 | 状态 | 新状态 |
---|---|---|---|
CUST001 | Jul-22 | 首次订单 | 首次订单 |
CUST001 | Aug-22 | 连续订单 | 连续订单 |
CUST001 | Sep-22 | 连续订单 | 连续订单 |
CUST001 | Oct-22 | 停止订单 | 停止订单 |
CUST001 | Nov-22 | N/A | 停止订单 |
CUST001 | Dec-22 | N/A | 停止订单 |
CUST001 | Jan-23 | N/A | 停止订单 |
CUST001 | Feb-23 | N/A | 停止订单 |
CUST001 | Mar-23 | 重新激活 | 停止订单 |
CUST001 | Apr-23 | 连续订单 | 停止订单 |
CUST001 | May-23 | 停止订单 | 停止订单 |
我有一个查询,为每个客户分配自他们的首次订单以来的连续“日期”,并根据他们的订单活动分配状态,基本上是上表中的前三列。我正在尝试编写一个脚本来获取新的状态列,该列应与状态相同,直到状态=“停止订单”,从那时起,它应始终是“停止订单”。
我尝试使用递归CTE(如下所示),使用LAG函数,但它不起作用。它只返回前一个状态作为新状态。
STOPPED_ORDER_ (CUSTOMER, DATE, STATUS, NEW_STATUS, COUNT_) AS
(
SELECT
CUSTOMER,
DATE,
STATUS,
LAG(STATUS) OVER (PARTITION BY CUSTOMER ORDER BY DATE) NEW_STATUS,
1 AS COUNT_
FROM ORDER_STATUS *-- order_status is another cte which assigns status and date *
UNION ALL
SELECT
CUSTOMER,
DATE,
CASE
WHEN NEW_STATUS = 'STOPPED ORDER' THEN 'STOPPED ORDER'
ELSE STATUS END STATUS,
NEW_STATUS,
COUNT_ + 1
FROM STOPPED_ORDER_
WHERE COUNT_ < 1
)
我尝试了不同版本的以下代码,但它不起作用。它始终返回以下结果。
客户 | 日期 | 状态 | 新状态 | COUNT_ |
---|---|---|---|---|
CUST001 | Jul-22 | 首次订单 | 1 | |
CUST001 | Aug-22 | 连续订单 | 首次订单 | 1 |
CUST001 | Sep-22 | 连续订单 | 连续订单 | 1 |
CUST001 | Oct-22 | 停止订单 | 连续订单 | 1 |
CUST001 | Nov-22 | N/A | 停止订单 | 1 |
CUST001 | Dec-22 | N/A | N/A | 1 |
CUST001 | Jan-23 | N/A | N/A | 1 |
CUST001 | Feb-23 | N/A | N/A | 1 |
CUST001 | Mar-23 | 重新激活 | N/A | 1 |
CUST001 | Apr-23 | 连续订单 | 重新激活 | 1 |
CUST001 | May-23 | 停止订单 | 连续订单 | 1 |
英文:
I'm trying to extract customers which have placed orders each month in a given period. I'm trying to include all customers up to the point they stop placing an order. continuous
I've created the result that I'm trying to extract, in the table below.
Customer | DATE | STATUS | NEW STATUS |
---|---|---|---|
CUST001 | Jul-22 | First Order | First Order |
CUST001 | Aug-22 | Continuous Order | Continuous Order |
CUST001 | Sep-22 | Continuous Order | Continuous Order |
CUST001 | Oct-22 | Stopped Order | Stopped Order |
CUST001 | Nov-22 | N/A | Stopped Order |
CUST001 | Dec-22 | N/A | Stopped Order |
CUST001 | Jan-23 | N/A | Stopped Order |
CUST001 | Feb-23 | N/A | Stopped Order |
CUST001 | Mar-23 | Reactivated | Stopped Order |
CUST001 | Apr-23 | Continuous Order | Stopped Order |
CUST001 | May-23 | Stopped Order | Stopped Order |
I have an query which assigns each customer a sequential "DATE" since their first order and assigns status based on their order activity, essentially the first three columns in the above table. I'm trying to write a script to get the New Status column which would be the same as status up to the point where status = 'Stopped Order" and from that point onward, it should always be Stopped Order.
I've tried to use a recursive CTE (copied below) using LAG function, but it doesn't work. It just returns the previous status as the new status.
STOPPED_ORDER_ (CUSTOMER, DATE, STATUS, NEW_STATUS, COUNT_) AS
(
SELECT
CUSTOMER,
DATE,
STATUS,
LAG(STATUS) OVER (PARTITION BY CUSTOMER ORDER BY DATE) NEW_STATUS,
1 AS COUNT_
FROM ORDER_STATUS *-- order_status is another cte which assigns status and date *
UNION ALL
SELECT
CUSTOMER,
DATE,
CASE
WHEN NEW_STATUS = 'STOPPED ORDER' THEN 'STOPPED ORDER'
ELSE STATUS END STATUS,
NEW_STATUS,
COUNT_ + 1
FROM STOPPED_ORDER_
WHERE COUNT_ < 1
)
I've tried different iterations of the following code but it doesn't work. It always returns the following result
Customer | DATE | STATUS | NEW STATUS | COUNT_ |
---|---|---|---|---|
CUST001 | Jul-22 | First Order | 1 | |
CUST001 | Aug-22 | Continuous Order | First Order | 1 |
CUST001 | Sep-22 | Continuous Order | Continuous Order | 1 |
CUST001 | Oct-22 | Stopped Order | Continuous Order | 1 |
CUST001 | Nov-22 | N/A | Stopped Order | 1 |
CUST001 | Dec-22 | N/A | N/A | 1 |
CUST001 | Jan-23 | N/A | N/A | 1 |
CUST001 | Feb-23 | N/A | N/A | 1 |
CUST001 | Mar-23 | Reactivated | N/A | 1 |
CUST001 | Apr-23 | Continuous Order | Reactivated | 1 |
CUST001 | May-23 | Stopped Order | Continuous Order | 1 |
答案1
得分: 1
您可以通过使用first_value
分析函数来实现这一点,如下所示:
WITH your_results (Customer, order_date, STATUS)
AS (SELECT 'CUST001', DATE '2022-07-01', 'First Order' FROM DUAL UNION ALL
SELECT 'CUST001', DATE '2022-08-01', 'Continuous Order' FROM DUAL UNION ALL
SELECT 'CUST001', DATE '2022-09-01', 'Continuous Order' FROM DUAL UNION ALL
SELECT 'CUST001', DATE '2022-10-01', 'Stopped Order' FROM DUAL UNION ALL
SELECT 'CUST001', DATE '2022-11-01', 'N/A' FROM DUAL UNION ALL
SELECT 'CUST001', DATE '2022-12-01', 'N/A' FROM DUAL UNION ALL
SELECT 'CUST001', DATE '2023-01-01', 'N/A' FROM DUAL UNION ALL
SELECT 'CUST001', DATE '2023-02-01', 'N/A' FROM DUAL UNION ALL
SELECT 'CUST001', DATE '2023-03-01', 'Reactivated' FROM DUAL UNION ALL
SELECT 'CUST001', DATE '2023-04-01', 'Continuous Order' FROM DUAL UNION ALL
SELECT 'CUST001', DATE '2023-05-01', 'Stopped Order' FROM DUAL)
SELECT customer,
order_date,
status,
NVL(first_value(CASE WHEN status = 'Stopped Order' THEN status END IGNORE NULLS) over (PARTITION BY customer order BY order_date), status) new_status
FROM your_results;
CUSTOMER ORDER_DATE STATUS NEW_STATUS
CUST001 01/07/2022 First Order First Order
CUST001 01/08/2022 Continuous Order Continuous Order
CUST001 01/09/2022 Continuous Order Continuous Order
CUST001 01/10/2022 Stopped Order Stopped Order
CUST001 01/11/2022 N/A Stopped Order
CUST001 01/12/2022 N/A Stopped Order
CUST001 01/01/2023 N/A Stopped Order
CUST001 01/02/2023 N/A Stopped Order
CUST001 01/03/2023 Reactivated Stopped Order
CUST001 01/04/2023 Continuous Order Stopped Order
CUST001 01/05/2023 Stopped Order Stopped Order
英文:
You can achieve this by using the first_value
analytic function, like so:
WITH your_results (Customer, order_date, STATUS)
AS (SELECT 'CUST001', DATE '2022-07-01', 'First Order' FROM DUAL UNION ALL
SELECT 'CUST001', DATE '2022-08-01', 'Continuous Order' FROM DUAL UNION ALL
SELECT 'CUST001', DATE '2022-09-01', 'Continuous Order' FROM DUAL UNION ALL
SELECT 'CUST001', DATE '2022-10-01', 'Stopped Order' FROM DUAL UNION ALL
SELECT 'CUST001', DATE '2022-11-01', 'N/A' FROM DUAL UNION ALL
SELECT 'CUST001', DATE '2022-12-01', 'N/A' FROM DUAL UNION ALL
SELECT 'CUST001', DATE '2023-01-01', 'N/A' FROM DUAL UNION ALL
SELECT 'CUST001', DATE '2023-02-01', 'N/A' FROM DUAL UNION ALL
SELECT 'CUST001', DATE '2023-03-01', 'Reactivated' FROM DUAL UNION ALL
SELECT 'CUST001', DATE '2023-04-01', 'Continuous Order' FROM DUAL UNION ALL
SELECT 'CUST001', DATE '2023-05-01', 'Stopped Order' FROM DUAL)
SELECT customer,
order_date,
status,
NVL(first_value(CASE WHEN status = 'Stopped Order' THEN status END IGNORE NULLS) over (PARTITION BY customer order BY order_date), status) new_status
FROM your_results;
CUSTOMER ORDER_DATE STATUS NEW_STATUS
-------- ----------- ---------------- ----------------
CUST001 01/07/2022 First Order First Order
CUST001 01/08/2022 Continuous Order Continuous Order
CUST001 01/09/2022 Continuous Order Continuous Order
CUST001 01/10/2022 Stopped Order Stopped Order
CUST001 01/11/2022 N/A Stopped Order
CUST001 01/12/2022 N/A Stopped Order
CUST001 01/01/2023 N/A Stopped Order
CUST001 01/02/2023 N/A Stopped Order
CUST001 01/03/2023 Reactivated Stopped Order
CUST001 01/04/2023 Continuous Order Stopped Order
CUST001 01/05/2023 Stopped Order Stopped Order
答案2
得分: 0
您可以使用 MATCH_RECOGNIZE
进行逐行模式匹配:
SELECT customer,
order_date,
status,
CASE cls
WHEN 'AFTER_STOP' THEN 'Stopped Order'
ELSE status
END AS new_status
FROM order_status
MATCH_RECOGNIZE(
PARTITION BY customer
ORDER BY order_date
MEASURES
classifier() AS cls
ALL ROWS PER MATCH
PATTERN ( first_order any_order* (stopped_order after_stop*)? )
DEFINE
first_order AS status = 'First Order',
any_order AS status != 'Stopped Order'
AND ADD_MONTHS(PREV(order_date), 1) = order_date,
stopped_order AS status = 'Stopped Order'
AND ADD_MONTHS(PREV(order_date), 1) = order_date,
after_stop AS ADD_MONTHS(PREV(order_date), 1) = order_date
);
对于示例数据:
CREATE TABLE ORDER_STATUS (Customer, order_date, STATUS) AS
SELECT 'CUST001', DATE '2022-07-01', 'First Order' FROM DUAL UNION ALL
SELECT 'CUST001', DATE '2022-08-01', 'Continuous Order' FROM DUAL UNION ALL
SELECT 'CUST001', DATE '2022-09-01', 'Continuous Order' FROM DUAL UNION ALL
SELECT 'CUST001', DATE '2022-10-01', 'Stopped Order' FROM DUAL UNION ALL
SELECT 'CUST001', DATE '2022-11-01', 'N/A' FROM DUAL UNION ALL
SELECT 'CUST001', DATE '2022-12-01', 'N/A' FROM DUAL UNION ALL
SELECT 'CUST001', DATE '2023-01-01', 'N/A' FROM DUAL UNION ALL
SELECT 'CUST001', DATE '2023-02-01', 'N/A' FROM DUAL UNION ALL
SELECT 'CUST001', DATE '2023-03-01', 'Reactivated' FROM DUAL UNION ALL
SELECT 'CUST001', DATE '2023-04-01', 'Continuous Order' FROM DUAL UNION ALL
SELECT 'CUST001', DATE '2023-05-01', 'Stopped Order' FROM DUAL;
输出:
CUSTOMER | ORDER_DATE | STATUS | NEW_STATUS |
---|---|---|---|
CUST001 | 2022-07-01 00:00:00 | First Order | First Order |
CUST001 | 2022-08-01 00:00:00 | Continuous Order | Continuous Order |
CUST001 | 2022-09-01 00:00:00 | Continuous Order | Continuous Order |
CUST001 | 2022-10-01 00:00:00 | Stopped Order | Stopped Order |
CUST001 | 2022-11-01 00:00:00 | N/A | Stopped Order |
CUST001 | 2022-12-01 00:00:00 | N/A | Stopped Order |
CUST001 | 2023-01-01 00:00:00 | N/A | Stopped Order |
CUST001 | 2023-02-01 00:00:00 | N/A | Stopped Order |
CUST001 | 2023-03-01 00:00:00 | Reactivated | Stopped Order |
CUST001 | 2023-04-01 00:00:00 | Continuous Order | Stopped Order |
CUST001 | 2023-05-01 00:00:00 | Stopped Order | Stopped Order |
英文:
You can use MATCH_RECOGNIZE
to perform row-by-row pattern matching:
SELECT customer,
order_date,
status,
CASE cls
WHEN 'AFTER_STOP' THEN 'Stopped Order'
ELSE status
END AS new_status
FROM order_status
MATCH_RECOGNIZE(
PARTITION BY customer
ORDER BY order_date
MEASURES
classifier() AS cls
ALL ROWS PER MATCH
PATTERN ( first_order any_order* (stopped_order after_stop*)? )
DEFINE
first_order AS status = 'First Order',
any_order AS status != 'Stopped Order'
AND ADD_MONTHS(PREV(order_date), 1) = order_date,
stopped_order AS status = 'Stopped Order'
AND ADD_MONTHS(PREV(order_date), 1) = order_date,
after_stop AS ADD_MONTHS(PREV(order_date), 1) = order_date
);
Which, for the sample data:
CREATE TABLE ORDER_STATUS (Customer, order_date, STATUS) AS
SELECT 'CUST001', DATE '2022-07-01', 'First Order' FROM DUAL UNION ALL
SELECT 'CUST001', DATE '2022-08-01', 'Continuous Order' FROM DUAL UNION ALL
SELECT 'CUST001', DATE '2022-09-01', 'Continuous Order' FROM DUAL UNION ALL
SELECT 'CUST001', DATE '2022-10-01', 'Stopped Order' FROM DUAL UNION ALL
SELECT 'CUST001', DATE '2022-11-01', 'N/A' FROM DUAL UNION ALL
SELECT 'CUST001', DATE '2022-12-01', 'N/A' FROM DUAL UNION ALL
SELECT 'CUST001', DATE '2023-01-01', 'N/A' FROM DUAL UNION ALL
SELECT 'CUST001', DATE '2023-02-01', 'N/A' FROM DUAL UNION ALL
SELECT 'CUST001', DATE '2023-03-01', 'Reactivated' FROM DUAL UNION ALL
SELECT 'CUST001', DATE '2023-04-01', 'Continuous Order' FROM DUAL UNION ALL
SELECT 'CUST001', DATE '2023-05-01', 'Stopped Order' FROM DUAL;
Outputs:
CUSTOMER | ORDER_DATE | STATUS | NEW_STATUS |
---|---|---|---|
CUST001 | 2022-07-01 00:00:00 | First Order | First Order |
CUST001 | 2022-08-01 00:00:00 | Continuous Order | Continuous Order |
CUST001 | 2022-09-01 00:00:00 | Continuous Order | Continuous Order |
CUST001 | 2022-10-01 00:00:00 | Stopped Order | Stopped Order |
CUST001 | 2022-11-01 00:00:00 | N/A | Stopped Order |
CUST001 | 2022-12-01 00:00:00 | N/A | Stopped Order |
CUST001 | 2023-01-01 00:00:00 | N/A | Stopped Order |
CUST001 | 2023-02-01 00:00:00 | N/A | Stopped Order |
CUST001 | 2023-03-01 00:00:00 | Reactivated | Stopped Order |
CUST001 | 2023-04-01 00:00:00 | Continuous Order | Stopped Order |
CUST001 | 2023-05-01 00:00:00 | Stopped Order | Stopped Order |
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论