Условие
Таблица Delivery(delivery_id, customer_id, order_date, customer_pref_delivery_date).
«Immediate» — order_date = customer_pref_delivery_date. «Scheduled» — иначе.
Для каждого клиента берём только его первый заказ (по order_date). Найдите долю клиентов, у которых первый заказ — immediate. Округлить до 2 знаков, в процентах (*100).
Решение
Через MIN(order_date) на клиента
WITH firsts AS (
SELECT customer_id, MIN(order_date) AS first_date
FROM Delivery
GROUP BY customer_id
),
joined AS (
SELECT d.*
FROM Delivery d
JOIN firsts f
ON f.customer_id = d.customer_id
AND f.first_date = d.order_date
)
SELECT
ROUND(
100.0 * AVG(CASE WHEN order_date = customer_pref_delivery_date THEN 1 ELSE 0 END),
2
) AS immediate_percentage
FROM joined;Через ROW_NUMBER()
WITH ranked AS (
SELECT
customer_id, order_date, customer_pref_delivery_date,
ROW_NUMBER() OVER (PARTITION BY customer_id ORDER BY order_date) AS rn
FROM Delivery
)
SELECT
ROUND(
100.0 * AVG(CASE WHEN order_date = customer_pref_delivery_date THEN 1 ELSE 0 END),
2
) AS immediate_percentage
FROM ranked
WHERE rn = 1;Подводные камни
- Тай-брейк по
order_date. Если в один день два заказа от клиента —MIN(order_date)даст обе строки. В задаче гарантировано один заказ в день, но в проде это рискованно — лучшеROW_NUMBERс детерминированнымORDER BY (order_date, delivery_id). 100.0 *, не100 *. В MySQL/Postgres целочисленное деление обрежет до 0.AVGvsSUM/COUNT. ДелатьAVG(CASE...)короче, но в некоторых СУБД (Oracle, SQL Server) поведение с булевыми отличается. Универсально —SUM(CASE ... THEN 1 ELSE 0 END)::DECIMAL / COUNT(*).
Эталонный ответ
ROW_NUMBER по клиенту с ORDER BY order_date, фильтр rn = 1, доля через AVG(CASE...) × 100, ROUND(..., 2).