Для сравнения метрик по неделям нужно получить начало предыдущей недели. Какое выражение вернёт начало предыдущей недели относительно current_date?
Работа с датами в SQL — это DATE_TRUNC, EXTRACT, интервалы, приведение типов и группировка по временным периодам. На собеседованиях часто просят посчитать метрику по неделям или месяцам, найти разницу между датами, или отфильтровать данные за скользящее окно. Ошибки в работе с таймзонами и форматами дат — частая причина провала на интервью.
Всего в этом разделе 20 вопросов. Каждый — с правильным ответом и кратким разбором теории. Разбито на 4 части по 5 вопросов.
1Как получить начало текущей недели (недельный бакет) по текущей дате в PostgreSQL?
A`DATE_TRUNC('day', current_date)`
B`DATE_TRUNC('month', current_date)`
C`current_date - INTERVAL '7 days'`
D`DATE_TRUNC('week', current_date)`
Ответ: `DATE_TRUNC('week', ...)` возвращает метку начала недели.
Функция `DATE_TRUNC` обрезает дату/время до начала указанного периода. Для недельных бакетов используйте `DATE_TRUNC('week', current_date)` (в PostgreSQL неделя начинается в понедельник).
2В таблице `events(user_id, created_at)` поле `created_at` имеет тип отметки времени с долями секунд. Как корректно посчитать количество событий по дням?
A`SELECT created_at, COUNT(*) FROM events GROUP BY created_at`: группировка по точной метке времени
B`SELECT DATE_TRUNC('day', created_at) AS day, COUNT(*) FROM events GROUP BY DATE_TRUNC('day', created_at)`
C`SELECT DATE_TRUNC('day', created_at) AS day, COUNT(*) FROM events`: запрос без `GROUP BY` по выражению дня
D`SELECT DATE_TRUNC('month', created_at) AS day, COUNT(*) FROM events GROUP BY DATE_TRUNC('month', created_at)`
Ответ: `DATE_TRUNC('day', ...)` обрезает время до начала дня и подходит для группировки по дням.
Для бакетирования по дням используйте `DATE_TRUNC('day', created_at)` и группируйте по этому же выражению. Группировка по самому `created_at` даст отдельную группу на каждую уникальную метку времени. Без `GROUP BY` по выражению дня запрос вернёт ошибку или сложит все события в одну строку. Группировка по месяцу — это уже другое разрешение, не дни.
3Нужно построить график регистраций по часам из таблицы пользователей со столбцом `created_at` типа `timestamp`. Какой бакет лучше использовать?
A`DATE_TRUNC('day', created_at)`: округление до начала дня без сохранения часовой детализации
B`EXTRACT(HOUR FROM created_at)`: извлекает только номер часа без сохранения даты события
C`DATE_TRUNC('minute', created_at)`: округление до минуты с очень мелкой шириной бакета
D`DATE_TRUNC('hour', created_at)`: округление момента до часа с сохранением даты события
Ответ: Часовой бакет правильно строится через `DATE_TRUNC('hour', ...)`, чтобы сохранились и дата, и час.
Если извлечь только номер часа через `EXTRACT(HOUR FROM created_at)`, потеряется дата и регистрации в один и тот же час разных дней попадут в один бакет. Усечение `DATE_TRUNC('hour', created_at)` сохраняет дату и обнуляет минуты с секундами, давая корректные часовые точки на временной оси. Округление до дня скроет внутридневную динамику, а до минуты раздробит график до неинтерпретируемой плотности. Поэтому именно часовое усечение подходит лучше всего.
4Нужно получить колонку `event_date` типа `date` из `created_at` (тип `timestamp`) для `JOIN` с календарной таблицей по дате. Какое выражение подойдёт лучше всего?
A`created_at::date`: прямое приведение `timestamp` к типу `date` для джойна с календарём
B`DATE_TRUNC('day', created_at)`: обрезает время до начала дня, но возвращает значение типа `timestamp`
C`EXTRACT(DAY FROM created_at)`: возвращает только число месяца как целое число `int`
D`DATE_TRUNC('month', created_at)::date`: округляет дату до начала месяца с приведением к `date`
Ответ: Если нужен именно тип `date`, используйте приведение `created_at::date`.
`DATE_TRUNC('day', created_at)` даёт начало дня, но возвращает `timestamp`, и для джойна потребуется дополнительное приведение. `EXTRACT(DAY FROM ...)` возвращает только число месяца, а `DATE_TRUNC('month', ...)` округляет до начала месяца, что меняет смысл ключа. Для джойна с календарём, где ключ обычно типа `date`, удобнее сразу получить `created_at::date`.
5Как сгруппировать заказы из таблицы `orders(created_at)` по кварталам с помощью `DATE_TRUNC`?
A`SELECT DATE_TRUNC('quarter', created_at) AS q, COUNT(*) FROM orders GROUP BY DATE_TRUNC('quarter', created_at);`
B`SELECT DATE_TRUNC('month', created_at) AS q, COUNT(*) FROM orders GROUP BY DATE_TRUNC('month', created_at);`
C`SELECT created_at + INTERVAL '3 months' AS q, COUNT(*) FROM orders GROUP BY created_at + INTERVAL '3 months';`
D`SELECT EXTRACT(QUARTER FROM created_at) AS q, COUNT(*) FROM orders GROUP BY EXTRACT(QUARTER FROM created_at);`
Ответ: `DATE_TRUNC('quarter', ...)` возвращает начало квартала и подходит для квартальных бакетов.
Для квартальной агрегации удобнее использовать `DATE_TRUNC('quarter', created_at)`, потому что результат — конкретная дата/время начала квартала (например, 1 января или 1 апреля). Это лучше для последующих `JOIN` и сортировок, чем просто номер квартала без года.