Условие
Таблицы:
Trips(id, client_id, driver_id, city_id, status, request_at)—status∈ {completed,cancelled_by_driver,cancelled_by_client}.Users(users_id, banned, role)—banned∈ {Yes,No},role∈ {client,driver,partner}.
Для каждого дня в интервале 2013-10-01..2013-10-03 посчитайте Cancellation Rate — долю отменённых поездок среди тех, где ни клиент, ни водитель не забанены. Округление до 2 знаков. Дни без не-забаненных поездок не показывать.
Решение
Подход
- Отфильтровать
Usersдо не-забаненных по двум ролям. - JOIN дважды (по client_id и driver_id) или EXISTS-фильтр.
- Условная агрегация
AVG(CASE WHEN status LIKE 'cancelled%' THEN 1 ELSE 0 END).
SELECT
request_at AS Day,
ROUND(
AVG(CASE WHEN status <> 'completed' THEN 1.0 ELSE 0 END),
2
) AS "Cancellation Rate"
FROM Trips t
WHERE request_at BETWEEN '2013-10-01' AND '2013-10-03'
AND client_id NOT IN (SELECT users_id FROM Users WHERE banned = 'Yes')
AND driver_id NOT IN (SELECT users_id FROM Users WHERE banned = 'Yes')
GROUP BY request_at
ORDER BY request_at;Эквивалент через JOIN
SELECT t.request_at AS Day,
ROUND(SUM(CASE WHEN status <> 'completed' THEN 1 ELSE 0 END)::DECIMAL
/ COUNT(*), 2) AS "Cancellation Rate"
FROM Trips t
JOIN Users uc ON uc.users_id = t.client_id AND uc.banned = 'No'
JOIN Users ud ON ud.users_id = t.driver_id AND ud.banned = 'No'
WHERE t.request_at BETWEEN '2013-10-01' AND '2013-10-03'
GROUP BY t.request_at;Подводные камни
status— две разных «отмены». Любаяcancelled_*идёт в числитель. Проще писатьstatus <> 'completed'.- NOT IN с NULL. Если в
Usersестьusers_id IS NULL,NOT INвернёт пусто. Если такое возможно —NOT EXISTSили фильтрWHERE users_id IS NOT NULL. - AVG vs SUM/COUNT.
AVG(CASE ... THEN 1.0 ELSE 0)неявно даёт долю — но в MySQL нужно1.0, иначе целочисленное деление.
Эталонный ответ
Отфильтровать забаненных клиентов и водителей, потом AVG(CASE WHEN status <> 'completed' THEN 1.0 ELSE 0 END). Округление до 2 знаков.