Сначала попробуйте сами: на собеседовании подсказки не будет.
Снова «gaps and islands», но по порядковым номерам. Нумеруем все заказы клиента по времени, затем среди отменённых нумеруем ещё раз. Для подряд идущих отмен разность двух номеров постоянна, для серии после пропуска она другая.
Группируем по клиенту и этой разности, HAVING count(*) >= 3 оставляет серии нужной длины, DISTINCT схлопывает клиентов с несколькими сериями. lower(status) из-за разного регистра в данных.
WITH numbered AS (SELECT customer_id, lower(status) = 'cancelled' AS cancelled, row_number() OVER (PARTITION BY customer_id ORDER BY created_at, order_id) AS rn FROM orders), runs AS (SELECT customer_id, rn - row_number() OVER (PARTITION BY customer_id ORDER BY rn) AS grp FROM numbered WHERE cancelled) SELECT DISTINCT customer_id FROM runs GROUP BY customer_id, grp HAVING count(*) >= 3