Задачи на даты SQL на собеседовании

Карьерник — квиз-тренажёр в Telegram с 1500+ задач и вопросов для собесов аналитика. SQL, Python, A/B, метрики. Бесплатно.

Зачем проверяют работу с датами

Аналитик постоянно работает с датами: отчёты по дням/неделям/месяцам, когорты, retention, когортный анализ. Частые подводные камни: таймзоны, NULL в датах, конец месяца.

Задачи с разборами

Задача 1. Выручка по месяцам

SELECT
    DATE_TRUNC('month', created_at) AS month,
    SUM(total) AS revenue,
    COUNT(*) AS orders
FROM orders
GROUP BY 1
ORDER BY 1;

Задача 2. Заказы за последние 7 дней

SELECT *
FROM orders
WHERE created_at >= CURRENT_DATE - INTERVAL '7 days';

Альтернативы:

  • MySQL: DATE_SUB(NOW(), INTERVAL 7 DAY)
  • ClickHouse: now() - INTERVAL 7 DAY

Задача 3. Пользователи, зарегистрировавшиеся на этой неделе

SELECT *
FROM users
WHERE DATE_TRUNC('week', created_at) = DATE_TRUNC('week', CURRENT_DATE);

Задача 4. Разница в днях между двумя датами

SELECT
    user_id,
    (MAX(created_at) - MIN(created_at))::INTERVAL AS activity_span,
    EXTRACT(DAY FROM (MAX(created_at) - MIN(created_at))) AS days_between
FROM orders
GROUP BY user_id;

В ClickHouse: dateDiff('day', min, max).


Задача 5. Возраст пользователей

SELECT
    user_id,
    EXTRACT(YEAR FROM AGE(birth_date)) AS age
FROM users;

MySQL:

TIMESTAMPDIFF(YEAR, birth_date, CURDATE())

Задача 6. Заказы в выходные

SELECT *
FROM orders
WHERE EXTRACT(DOW FROM created_at) IN (0, 6);  -- 0=Sunday, 6=Saturday

Внимание: разные СУБД считают DOW по-разному:

  • Postgres: 0=Sunday, 6=Saturday
  • MySQL: 1=Sunday, 7=Saturday (с DAYOFWEEK)
  • MySQL: 0=Monday, 6=Sunday (с WEEKDAY)

Задача 7. Последний день месяца

SELECT
    DATE_TRUNC('month', created_at) + INTERVAL '1 month' - INTERVAL '1 day' AS last_day
FROM orders;

Или в Postgres:

SELECT (DATE_TRUNC('month', created_at) + INTERVAL '1 month - 1 day')::DATE;

Задача 8. Первое и последнее событие пользователя

SELECT
    user_id,
    MIN(created_at) AS first_event,
    MAX(created_at) AS last_event,
    MAX(created_at) - MIN(created_at) AS activity_duration
FROM events
GROUP BY user_id;

Задача 9. Клиенты, не совершавшие покупок 30+ дней

SELECT
    u.user_id,
    MAX(o.created_at) AS last_order,
    CURRENT_DATE - MAX(o.created_at)::DATE AS days_inactive
FROM users u
LEFT JOIN orders o ON o.user_id = u.user_id
GROUP BY u.user_id
HAVING MAX(o.created_at) IS NULL
    OR CURRENT_DATE - MAX(o.created_at)::DATE > 30;

Задача 10. Количество дней активности каждого пользователя

SELECT
    user_id,
    COUNT(DISTINCT DATE(event_at)) AS active_days
FROM events
GROUP BY user_id;

Задача 11. Когорты по месяцу регистрации + retention

WITH first_order AS (
    SELECT
        user_id,
        DATE_TRUNC('month', MIN(created_at)) AS cohort_month
    FROM orders GROUP BY user_id
),
activity AS (
    SELECT DISTINCT
        user_id,
        DATE_TRUNC('month', created_at) AS activity_month
    FROM orders
)
SELECT
    f.cohort_month,
    a.activity_month,
    EXTRACT(MONTH FROM AGE(a.activity_month, f.cohort_month)) AS months_since_signup,
    COUNT(DISTINCT a.user_id) AS active_users
FROM first_order f
JOIN activity a ON a.user_id = f.user_id
GROUP BY 1, 2
ORDER BY 1, 2;

Задача 12. Генерация календаря дат

Postgres:

SELECT generate_series('2026-01-01'::DATE, '2026-12-31'::DATE, '1 day')::DATE AS day;

MySQL:

WITH RECURSIVE days AS (
    SELECT DATE('2026-01-01') AS day
    UNION ALL
    SELECT day + INTERVAL 1 DAY FROM days WHERE day < '2026-12-31'
)
SELECT * FROM days;

Задача 13. Разница в рабочих часах

Количество рабочих дней между двумя датами — сложная задача. Приблизительно:

SELECT
    order_id,
    COUNT(*) AS workdays_between
FROM orders o
JOIN generate_series(
    o.created_at::DATE,
    o.delivered_at::DATE,
    INTERVAL '1 day'
) day
WHERE EXTRACT(DOW FROM day) NOT IN (0, 6)
GROUP BY o.order_id;

Задача 14. Форматирование даты

Postgres:

TO_CHAR(created_at, 'DD.MM.YYYY')           -- '21.04.2026'
TO_CHAR(created_at, 'YYYY-MM-DD HH24:MI')   -- '2026-04-21 10:30'
TO_CHAR(created_at, 'Mon')                  -- 'Apr'

MySQL:

DATE_FORMAT(created_at, '%d.%m.%Y')

Задача 15. Время до первой покупки

SELECT
    u.user_id,
    u.created_at AS signup,
    MIN(o.created_at) AS first_order,
    EXTRACT(EPOCH FROM (MIN(o.created_at) - u.created_at)) / 86400 AS days_to_first_order
FROM users u
LEFT JOIN orders o ON o.user_id = u.user_id
GROUP BY u.user_id, u.created_at;

Частые ошибки

Ошибка 1. Таймзоны

-- если сервер UTC, а Метрика в МСК — сдвиг 3 часа
-- аналитический день может быть разным
DATE_TRUNC('day', created_at)  -- UTC
DATE_TRUNC('day', created_at AT TIME ZONE 'Europe/Moscow')  -- МСК

Всегда уточняйте таймзону при агрегации по дням.

Ошибка 2. Месяц — переменная длина

«Предыдущий месяц» нельзя тупо CURRENT_DATE - INTERVAL '30 days'. Правильно:

DATE_TRUNC('month', CURRENT_DATE - INTERVAL '1 month')

Ошибка 3. BETWEEN с датами

-- исключает часть дня
WHERE created_at BETWEEN '2026-01-01' AND '2026-01-31'
-- 2026-01-31 23:00 не попадёт

-- лучше
WHERE created_at >= '2026-01-01'
  AND created_at <  '2026-02-01'

Ошибка 4. NULL в датах

-- NULL = любое значение = UNKNOWN
WHERE last_login < CURRENT_DATE - INTERVAL '30 days'
-- пропустит пользователей, у которых last_login IS NULL

-- лучше
WHERE last_login IS NULL
   OR last_login < CURRENT_DATE - INTERVAL '30 days'

Ошибка 5. Неделя как DOW

Начало недели зависит от страны:

  • США: воскресенье
  • Россия/Европа: понедельник
-- Postgres ISO (понедельник)
DATE_TRUNC('week', created_at)

Связанные темы

FAQ

Какая функция округляет дату до месяца?

Postgres/Snowflake: DATE_TRUNC('month', date). MySQL: DATE_FORMAT(date, '%Y-%m-01').

Как посчитать разницу в днях?

Postgres: (date1 - date2)::INTEGER. MySQL: DATEDIFF(date1, date2). ClickHouse: dateDiff('day', d1, d2).

Как учитывать таймзоны?

Храните в UTC, конвертируйте при выборке: created_at AT TIME ZONE 'Europe/Moscow'.

DATE_TRUNC или EXTRACT?

DATE_TRUNC — для группировки (DATE_TRUNC('month', d) → '2026-04-01'). EXTRACT — для конкретной части (EXTRACT(MONTH FROM d) → 4).


Тренируйте SQL — откройте тренажёр с 200+ задачами и 1500+ вопросами для собесов аналитиков.