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_idorder_dateproduct_namepricememberranking
A2021-01-01sushi10Nnull
A2021-01-01curry15Nnull
A2021-01-07curry15Y1
A2021-01-10ramen12Y2
A2021-01-11ramen12Y3
A2021-01-11ramen12Y3
B2021-01-01curry15Nnull
B2021-01-02curry15Nnull
B2021-01-04sushi10Nnull
B2021-01-11sushi10Y1
B2021-01-16ramen12Y2
B2021-02-01ramen12Y3
C2021-01-01ramen12Nnull
C2021-01-01ramen12Nnull
C2021-01-07ramen12Nnull

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_idorder_dateproduct_namepricememberranking
A2021-01-01sushi10Nnull
A2021-01-01curry15Nnull
A2021-01-07curry15Y1
A2021-01-10ramen12Y2
A2021-01-11ramen12Y3
A2021-01-11ramen12Y3
B2021-01-01curry15Nnull
B2021-01-02curry15Nnull
B2021-01-04sushi10Nnull
B2021-01-11sushi10Y1
B2021-01-16ramen12Y2
B2021-02-01ramen12Y3
C2021-01-01ramen12Nnull
C2021-01-01ramen12Nnull
C2021-01-07ramen12Nnull