Simplified Query Date Based on Category

Viewed 59

I have data:

date_key    customer_id  product_variant_final  frequency
2022-03-02  1            a                       1
2022-04-01  2            b                       2
2022-05-02  3            c                       2
......

I want to simplified this query. The logic are:

  1. I want to know the frequency
  2. Select max when frequency 1 and 2, then get column date_pertama and date_kedua
  3. I want to know gap between date_pertama and date_kedua. In there the name is selisih
  4. Categorize selisih based on some condition

I try this query. But maybe it can be simplified.

SELECT customer_id,
       product_variant_final,
       date_pertama,
       date_kedua,
       selisih, 
       CASE WHEN selisih = 0 THEN "Same Day"
            WHEN selisih BETWEEN -1 AND -7 THEN "1-7 Days"
            WHEN selisih BETWEEN -8 AND -14 THEN "8-14 Days"
            WHEN selisih BETWEEN -15 AND -21 THEN "15-21 Days"
            WHEN selisih < -21 THEN "21+ Days"
       END AS frequency_purchase 
FROM(
SELECT customer_id,
       product_variant_final,
       date_pertama,
       date_kedua,
       DATE_DIFF(date_pertama, date_kedua, DAY) selisih
FROM(
SELECT customer_id,
       product_variant_final,
       MAX(CASE WHEN frequency=1 then date_key ELSE NULL END) date_pertama,
       MAX(CASE WHEN frequency=2 then date_key ELSE NULL END) date_kedua
FROM(
SELECT date_key,
       customer_id,
       product_variant_final,
       ROW_NUMBER() OVER(PARTITION BY customer_id, product_variant_final ORDER BY date_key) frequency
FROM final_data_variant 
WHERE variant_type = "variant" 
      AND customer_id IN(SELECT customer_id FROM total_purchased_data WHERE total_purchased >= 2)
) 
GROUP BY 1,2
))
1 Answers

Your query uses the calculations from the inner SELECT Statement in the outer ones. Get the first and 2nd date of an order of each customer. Then calculate the difference selisih in days between these dates. Then categorize that value to frequency_purchase.

These nested SELECT structure can be eliminated by using UDF written in SQL, because no further aggregation or joins are done here. The names of the UDF are based on the final calculated columns. The UDF returns a struct with all needed columns, this is extracted by the (...).*

create temp function frequency_purchase(selisih int64) as 
(CASE WHEN selisih>0 THEN "ERROR: selisih must be negativ "
WHEN selisih = 0 THEN "Same Day"
            WHEN selisih >= -7  THEN "1-7 Days"
            WHEN selisih >= -14 THEN "8-14 Days"
            WHEN selisih >= -21 THEN "15-21 Days"
            WHEN selisih < -21 THEN "21+ Days"
            else "ERROR"
       END);
create temp function selisih(dates array<date>) as 
  (
    struct(dates[safe_offset(0)] as date_pertama, dates[safe_offset(1)] as date_kedua,
    DATE_DIFF(dates[safe_offset(0)], dates[safe_offset(1)], DAY) as selisih,
    frequency_purchase( DATE_DIFF(dates[safe_offset(0)], dates[safe_offset(1)], DAY)) AS frequency_purchase 
    )
    );
WITH final_data_variant as 
(SELECT date_sub(current_date(),interval a day) as date_key, cast(rand()*5 as int64)   customer_id,1  product_variant_final
from unnest(generate_array(0,60)) a
)

SELECT customer_id,
       product_variant_final,
       selisih(array_agg(date_key ORDER BY date_key limit 2)  ).* # Extract struct
FROM(
SELECT date_key,
       customer_id,
       product_variant_final  
FROM final_data_variant 
#WHERE variant_type = "variant" AND customer_id IN(SELECT customer_id FROM total_purchased_data WHERE total_purchased >= 2)
) 
GROUP BY 1,2

The Where condition is quoted out, because I do not have the data table for this one.

Related