SQL · hard · Asked at Airbnb
Asked in Data Analyst interviews, in the SQL round.
Use PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY amount) where supported.
Where the database has it, use the ordered-set aggregate:
SELECT PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY amount) AS median_order_value
FROM orders;
PERCENTILE_CONT interpolates between the two middle values on an even count; PERCENTILE_DISC returns an actual value from the data. Say which one you mean.
MySQL has neither, so the portable answer is window functions: number the rows from both ends and keep the middle one or two.
SELECT AVG(amount) AS median_order_value
FROM (
SELECT
amount,
ROW_NUMBER() OVER (ORDER BY amount) AS rn,
COUNT(*) OVER () AS cnt
FROM orders
) t
WHERE rn IN ((cnt + 1) / 2, (cnt + 2) / 2);
Integer division makes the two expressions equal on an odd count (one middle row) and adjacent on an even count (two rows, averaged). Median per group is the same query with PARTITION BY added to both windows.
AVG(amount), offered as an approximation. The median is asked for precisely because order values are skewed.