Real-world data omits month-product combinations with no sales. Dashboard APIs often need a complete matrix of every month × every product, with zero for no sales, which you can build with CROSS JOIN + LEFT JOIN + COALESCE.
-- CROSS JOIN: generate every combination of two tables (Cartesian product) SELECT m.month, p.product_id FROM months m CROSS JOIN products p -- 3 rows in months × 4 rows in products = all 12 combinations -- COALESCE: return the first non-NULL value COALESCE(sales, 0) -- Return 0 when sales is NULL (replace no sales with zero)
Using the sales and products tables below, create a sales matrix for every month from 2024-01 through 2024-03 × every product.
Fill combinations with no sales with 0. Generate the month list in a CTE.
| sale_month | product_id | amount |
|---|---|---|
| 2024-01 | P01 | 30000 |
| 2024-01 | P02 | 45000 |
| 2024-02 | P01 | 50000 |
| 2024-03 | P02 | 20000 |
| 2024-03 | P03 | 35000 |
| product_id | product_name |
|---|---|
| P01 | Apple |
| P02 | Banana |
| P03 | Orange |
| sale_month | product_id | product_name | total_sales |
|---|---|---|---|
| 2024-01 | P01 | Apple | 30000 |
| 2024-01 | P02 | Banana | 45000 |
| 2024-01 | P03 | Orange | 0 |
| 2024-02 | P01 | Apple | 50000 |
| 2024-02 | P02 | Banana | 0 |
| 2024-02 | P03 | Orange | 0 |
| 2024-03 | P01 | Apple | 0 |
| 2024-03 | P02 | Banana | 20000 |
| 2024-03 | P03 | Orange | 35000 |
- Read every model answer, explanation and table-transition visualization across all 48 advanced sets (240 questions)
- One-time purchase — no subscription. Future advanced sets are included
- Background, problem and expected output stay free
Production APIs often need compound conditions such as “made at least two purchases, spent at least 50,000 in total, and made the latest purchase on or after a specified date.” Packing all of this into one query makes maintenance difficult. Decomposing the logic into CTE steps lets you test and modify each condition independently.
COUNT(*) >= 2, use HAVING. WHERE operates before grouping at the row level; HAVING operates after grouping at the aggregate level.Using the orders and users tables below, build an API query with CTEs that extracts users who meet all three conditions, decomposing the logic into steps.
Condition ①: at least 2 purchases in total
Condition ②: at least 50,000 in total purchases
Condition ③: latest purchase date on or after 2024-03-01
| user_id | user_name |
|---|---|
| U01 | Alice |
| U02 | Bob |
| U03 | Carol |
| U04 | Dave |
| U05 | Eve |
| order_id | user_id | amount | order_date |
|---|---|---|---|
| 1 | U01 | 20000 | 2024-01-15 |
| 2 | U01 | 35000 | 2024-03-10 |
| 3 | U02 | 60000 | 2024-02-20 |
| 4 | U03 | 15000 | 2024-01-05 |
| 5 | U03 | 25000 | 2024-03-22 |
| 6 | U04 | 80000 | 2024-03-01 |
| 7 | U04 | 10000 | 2024-04-05 |
| 8 | U05 | 12000 | 2024-02-14 |
| user_id | user_name | order_count | total_amount | last_order_date |
|---|---|---|---|---|
| U01 | Alice | 2 | 55000 | 2024-03-10 |
| U04 | Dave | 2 | 90000 | 2024-04-05 |
Note: Bob fails condition ① with only 1 purchase / Carol fails condition ② with a total of 40000 / Eve fails with 1 purchase and condition ③
- Read every model answer, explanation and table-transition visualization across all 48 advanced sets (240 questions)
- One-time purchase — no subscription. Future advanced sets are included
- Background, problem and expected output stay free
SUM() OVER (ORDER BY ...) is a window function that calculates the total through the current row (running total). Beyond monthly sales trends, it powers progress APIs that report how far you have advanced toward an annual target.
SUM(sales) OVER ( ORDER BY order_month -- Sort by month, then calculate the running total ROWS BETWEEN UNBOUNDED PRECEDING -- ROWS BETWEEN: specify the range of rows to aggregate AND CURRENT ROW -- From the first row through the current row = running total ) AS cumulative_sales
UNBOUNDED means there is no limit before the edge of the partition.Using the monthly_sales table below, build an API query that calculates monthly sales, cumulative sales, and the achievement rate (%) against an annual target of 800,000.
Aggregate by month in a CTE, then calculate the running total and achievement rate in the outer SELECT.
| order_month | sales |
|---|---|
| 2024-01 | 95000 |
| 2024-02 | 120000 |
| 2024-03 | 108000 |
| 2024-04 | 145000 |
| 2024-05 | 132000 |
| 2024-06 | 160000 |
| order_month | monthly_sales | cumulative_sales | achievement_rate |
|---|---|---|---|
| 2024-01 | 95000 | 95000 | 11.9 |
| 2024-02 | 120000 | 215000 | 26.9 |
| 2024-03 | 108000 | 323000 | 40.4 |
| 2024-04 | 145000 | 468000 | 58.5 |
| 2024-05 | 132000 | 600000 | 75.0 |
| 2024-06 | 160000 | 760000 | 95.0 |
- Read every model answer, explanation and table-transition visualization across all 48 advanced sets (240 questions)
- One-time purchase — no subscription. Future advanced sets are included
- Background, problem and expected output stay free
LEAD(col, n) is the inverse of LAG and returns the value n rows after the current row. Use it to calculate when the next purchase occurs and how many days elapse until it.
LEAD(order_date, 1) OVER ( PARTITION BY user_id -- Reference the next row independently for each user ORDER BY order_date -- Return order_date from the next row in date order ) -- The last purchase row has no next row and becomes NULL
date2 - date1 returns the number of days as an INTEGER. BigQuery uses DATE_DIFF(date2, date1, DAY). A date difference containing NULL also returns NULL through NULL propagation.Using the purchase_log table below, calculate the next purchase date and purchase interval in days for every purchase by every user.
For a user's last purchase, which has no next purchase, return NULL for both next_date and days_to_next.
| log_id | user_id | order_date |
|---|---|---|
| 1 | U01 | 2024-01-10 |
| 2 | U01 | 2024-02-15 |
| 3 | U01 | 2024-04-01 |
| 4 | U02 | 2024-01-20 |
| 5 | U02 | 2024-03-05 |
| 6 | U03 | 2024-02-28 |
| user_id | order_date | next_date | days_to_next |
|---|---|---|---|
| U01 | 2024-01-10 | 2024-02-15 | 36 |
| U01 | 2024-02-15 | 2024-04-01 | 46 |
| U01 | 2024-04-01 | NULL | NULL |
| U02 | 2024-01-20 | 2024-03-05 | 45 |
| U02 | 2024-03-05 | NULL | NULL |
| U03 | 2024-02-28 | NULL | NULL |
- Read every model answer, explanation and table-transition visualization across all 48 advanced sets (240 questions)
- One-time purchase — no subscription. Future advanced sets are included
- Background, problem and expected output stay free
RFM analysis is a marketing method that evaluates customers on three axes—Recency (days since the last purchase), Frequency (number of purchases), and Monetary (purchase amount)—and assigns them to segments. Splitting the process across multiple CTEs isolates the calculation logic for each score.
-- CASE WHEN: transform a value with conditional branches (like if / else if) CASE WHEN recency_days < 30 THEN 3 -- 3 points if under 30 days WHEN recency_days < 90 THEN 2 -- 2 points if under 90 days ELSE 1 -- Otherwise, 1 point END
CURRENT_DATE. In production, accept the reference date as a parameter.Using the orders table below, build an API query with multiple CTEs that calculates RFM scores of 1–3 points each and classifies users into VIP, Standard, or Dormant segments.
Use 2024-04-01 as the reference date. Scoring criteria:
R (Recency): <30 days=3, <90 days=2, otherwise=1
F (Frequency): ≥3 purchases=3, ≥2 purchases=2, otherwise=1
M (Monetary): ≥100000=3, ≥50000=2, otherwise=1
Segment: R+F+M ≥8=VIP, ≥5=Standard, otherwise=Dormant
| order_id | user_id | amount | order_date |
|---|---|---|---|
| 1 | U01 | 30000 | 2024-01-10 |
| 2 | U01 | 50000 | 2024-02-20 |
| 3 | U01 | 40000 | 2024-03-25 |
| 4 | U02 | 80000 | 2024-03-15 |
| 5 | U02 | 60000 | 2024-03-28 |
| 6 | U03 | 120000 | 2023-12-01 |
| 7 | U04 | 15000 | 2024-03-30 |
| 8 | U04 | 20000 | 2024-03-31 |
| 9 | U04 | 18000 | 2024-04-01 |
| user_id | recency | freq | monetary | R | F | M | total | segment |
|---|---|---|---|---|---|---|---|---|
| U04 | 0 days | 3 purchases | 53000 | 3 | 3 | 2 | 8 | VIP |
| U02 | 4 days | 2 purchases | 140000 | 3 | 2 | 3 | 8 | VIP |
| U01 | 7 days | 3 purchases | 120000 | 3 | 3 | 3 | 9 | VIP |
| U03 | 122 days | 1 purchase | 120000 | 1 | 1 | 3 | 5 | Standard |
Note: R is calculated from the elapsed days as of the reference date 2024-04-01. U03 has recency 122 days (not <90 days, so 1 point). total_score≥8=VIP, ≥5=Standard, otherwise=Dormant.
- Read every model answer, explanation and table-transition visualization across all 48 advanced sets (240 questions)
- One-time purchase — no subscription. Future advanced sets are included
- Background, problem and expected output stay free