Rank All The Things
Danny also requires further information about the ranking of customer products, but he purposely does not need the ranking for non-member purchases so he expects null ranking values for the records when customers are not yet part of the loyalty program.
customer_id | order_date | product_name | price | member | ranking |
---|---|---|---|---|---|
A | 2021-01-01 | curry | 15 | N | null |
A | 2021-01-01 | sushi | 10 | N | null |
A | 2021-01-07 | curry | 15 | Y | 1 |
A | 2021-01-10 | ramen | 12 | Y | 2 |
A | 2021-01-11 | ramen | 12 | Y | 3 |
A | 2021-01-11 | ramen | 12 | Y | 3 |
B | 2021-01-01 | curry | 15 | N | null |
B | 2021-01-02 | curry | 15 | N | null |
B | 2021-01-04 | sushi | 10 | N | null |
B | 2021-01-11 | sushi | 10 | Y | 1 |
B | 2021-01-16 | ramen | 12 | Y | 2 |
B | 2021-02-01 | ramen | 12 | Y | 3 |
C | 2021-01-01 | ramen | 12 | N | null |
C | 2021-01-01 | ramen | 12 | N | null |
C | 2021-01-07 | ramen | 12 | N | null |
Solution
WITH sales_with_membership AS (
SELECT sales.customer_id,
order_date,
product_name,
price,
(
CASE order_date >= join_date
WHEN TRUE THEN 'Y'
ELSE 'N'
END
) member
FROM sales
LEFT OUTER JOIN members ON sales.customer_id = members.customer_id
INNER JOIN menu ON sales.product_id = menu.product_id
)
SELECT *,
(
CASE member
WHEN 'N' THEN NULL
ELSE
DENSE_RANK() OVER (PARTITION BY customer_id, member ORDER BY order_date)
END
) ranking
FROM sales_with_membership
ORDER BY customer_id, order_date;
customer_id | order_date | product_name | price | member | ranking |
---|---|---|---|---|---|
A | 2021-01-01 | sushi | 10 | N | null |
A | 2021-01-01 | curry | 15 | N | null |
A | 2021-01-07 | curry | 15 | Y | 1 |
A | 2021-01-10 | ramen | 12 | Y | 2 |
A | 2021-01-11 | ramen | 12 | Y | 3 |
A | 2021-01-11 | ramen | 12 | Y | 3 |
B | 2021-01-01 | curry | 15 | N | null |
B | 2021-01-02 | curry | 15 | N | null |
B | 2021-01-04 | sushi | 10 | N | null |
B | 2021-01-11 | sushi | 10 | Y | 1 |
B | 2021-01-16 | ramen | 12 | Y | 2 |
B | 2021-02-01 | ramen | 12 | Y | 3 |
C | 2021-01-01 | ramen | 12 | N | null |
C | 2021-01-01 | ramen | 12 | N | null |
C | 2021-01-07 | ramen | 12 | N | null |
Walkthrough
Most of the data remains the same from the previous bonus question, except for the addition of a new column that stores the ranking of the items per customer.
SELECT sales.customer_id,
order_date,
product_name,
price,
(
CASE order_date >= join_date
WHEN TRUE THEN 'Y'
ELSE 'N'
END
) member
FROM sales
LEFT OUTER JOIN members ON sales.customer_id = members.customer_id
INNER JOIN menu ON sales.product_id = menu.product_id;
Now, we rank the rows by partitioning over (customer_id, member)
.
WITH sales_with_membership AS (
SELECT sales.customer_id,
order_date,
product_name,
price,
(
CASE order_date >= join_date
WHEN TRUE THEN 'Y'
ELSE 'N'
END
) member
FROM sales
LEFT OUTER JOIN members ON sales.customer_id = members.customer_id
INNER JOIN menu ON sales.product_id = menu.product_id
)
SELECT *,
(
CASE member
WHEN 'N' THEN NULL
ELSE
DENSE_RANK() OVER (PARTITION BY customer_id, member ORDER BY order_date)
END
) ranking
FROM sales_with_membership
ORDER BY customer_id, order_date;
customer_id | order_date | product_name | price | member | ranking |
---|---|---|---|---|---|
A | 2021-01-01 | sushi | 10 | N | null |
A | 2021-01-01 | curry | 15 | N | null |
A | 2021-01-07 | curry | 15 | Y | 1 |
A | 2021-01-10 | ramen | 12 | Y | 2 |
A | 2021-01-11 | ramen | 12 | Y | 3 |
A | 2021-01-11 | ramen | 12 | Y | 3 |
B | 2021-01-01 | curry | 15 | N | null |
B | 2021-01-02 | curry | 15 | N | null |
B | 2021-01-04 | sushi | 10 | N | null |
B | 2021-01-11 | sushi | 10 | Y | 1 |
B | 2021-01-16 | ramen | 12 | Y | 2 |
B | 2021-02-01 | ramen | 12 | Y | 3 |
C | 2021-01-01 | ramen | 12 | N | null |
C | 2021-01-01 | ramen | 12 | N | null |
C | 2021-01-07 | ramen | 12 | N | null |