← Назад к вопросам

Объясните принцип работы CASE WHEN в SQL. Приведите примеры использования.

1.0 Junior🔥 261 комментариев
#SQL и базы данных

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI26 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

CASE WHEN в SQL

CASE WHEN — это условный оператор в SQL, который позволяет выбирать значения на основе условий. Это одна из самых мощных и часто используемых конструкций в аналитических запросах.

Синтаксис

CASE 
  WHEN условие_1 THEN значение_1
  WHEN условие_2 THEN значение_2
  ...
  ELSE значение_по_умолчанию
END

Соответствия проверяются по порядку. Как только одно условие истинно, возвращается соответствующее значение и остальные не проверяются.

Простые примеры

Пример 1: Категоризация пользователей

SELECT 
  user_id,
  revenue,
  CASE 
    WHEN revenue >= 1000 THEN 'VIP'
    WHEN revenue >= 500 THEN 'Premium'
    WHEN revenue >= 100 THEN 'Regular'
    ELSE 'Low-value'
  END as user_segment
FROM user_revenue
ORDER BY revenue DESC;

Пример 2: Сегментирование по возрасту

SELECT 
  user_id,
  age,
  CASE 
    WHEN age < 18 THEN 'Teen'
    WHEN age < 25 THEN 'Young Adult'
    WHEN age < 35 THEN 'Adult'
    WHEN age < 50 THEN 'Middle-aged'
    ELSE 'Senior'
  END as age_group
FROM users;

Использование CASE с агрегатными функциями

Это очень мощная техника для подсчета разных категорий в одном запросе.

Пример 3: Подсчет конверсии по типам устройств

SELECT 
  DATE_TRUNC('day', created_at) as day,
  COUNT(*) as total_events,
  SUM(CASE WHEN converted = true THEN 1 ELSE 0 END) as conversions,
  COUNT(DISTINCT CASE WHEN device_type = 'mobile' THEN user_id END) as mobile_users,
  COUNT(DISTINCT CASE WHEN device_type = 'desktop' THEN user_id END) as desktop_users,
  ROUND(
    SUM(CASE WHEN converted = true THEN 1 ELSE 0 END)::float / COUNT(*) * 100,
    2
  ) as conversion_rate
FROM events
GROUP BY DATE_TRUNC('day', created_at)
ORDER BY day DESC;

Пример 4: Анализ воронки конверсии

SELECT 
  COUNT(DISTINCT CASE WHEN event_type = 'view' THEN user_id END) as viewed,
  COUNT(DISTINCT CASE WHEN event_type = 'add_to_cart' THEN user_id END) as added_to_cart,
  COUNT(DISTINCT CASE WHEN event_type = 'checkout' THEN user_id END) as started_checkout,
  COUNT(DISTINCT CASE WHEN event_type = 'purchase' THEN user_id END) as purchased
FROM events
WHERE created_at >= DATE_TRUNC('month', NOW() - INTERVAL '1 month')
  AND created_at < DATE_TRUNC('month', NOW());

CASE с математическими операциями

Пример 5: Динамический расчет с условиями

SELECT 
  order_id,
  amount,
  CASE 
    WHEN amount >= 1000 THEN amount * 0.9  -- 10% скидка
    WHEN amount >= 500 THEN amount * 0.95   -- 5% скидка
    ELSE amount                              -- без скидки
  END as final_amount,
  CASE 
    WHEN amount >= 1000 THEN amount * 0.1
    WHEN amount >= 500 THEN amount * 0.05
    ELSE 0
  END as discount_amount
FROM orders;

Простая форма CASE (Simple CASE)

Так же есть более простая форма, когда сравниваем одно значение с несколькими вариантами.

-- Полная форма (Searched CASE)
SELECT 
  CASE 
    WHEN country = 'US' THEN 'North America'
    WHEN country = 'CA' THEN 'North America'
    WHEN country = 'RU' THEN 'Europe/Asia'
    ELSE 'Other'
  END as region
FROM users;

-- Простая форма (Simple CASE)
SELECT 
  CASE country
    WHEN 'US' THEN 'North America'
    WHEN 'CA' THEN 'North America'
    WHEN 'RU' THEN 'Europe/Asia'
    ELSE 'Other'
  END as region
FROM users;

Обе формы эквивалентны в этом примере, но полная форма более универсальна.

Практические примеры для Product Analytics

Пример 6: RFM анализ (Recency, Frequency, Monetary)

WITH user_metrics AS (
  SELECT 
    user_id,
    MAX(created_at) as last_purchase,
    COUNT(*) as purchase_count,
    SUM(amount) as total_spent
  FROM orders
  WHERE created_at >= NOW() - INTERVAL '1 year'
  GROUP BY user_id
)
SELECT 
  user_id,
  CASE 
    WHEN (NOW() - last_purchase) <= INTERVAL '30 days' THEN 'Recency: High'
    WHEN (NOW() - last_purchase) <= INTERVAL '90 days' THEN 'Recency: Medium'
    ELSE 'Recency: Low'
  END as recency_score,
  CASE 
    WHEN purchase_count >= 10 THEN 'Frequency: High'
    WHEN purchase_count >= 5 THEN 'Frequency: Medium'
    ELSE 'Frequency: Low'
  END as frequency_score,
  CASE 
    WHEN total_spent >= 1000 THEN 'Monetary: High'
    WHEN total_spent >= 500 THEN 'Monetary: Medium'
    ELSE 'Monetary: Low'
  END as monetary_score
FROM user_metrics
ORDER BY total_spent DESC;

Пример 7: Когортный анализ с условной классификацией

WITH cohorts AS (
  SELECT 
    DATE_TRUNC('week', signup_date) as signup_cohort,
    user_id,
    (NOW() - signup_date)::int / 7 as weeks_since_signup,
    CASE 
      WHEN last_active_date >= NOW() - INTERVAL '7 days' THEN 1
      ELSE 0
    END as is_active
  FROM users
  WHERE signup_date >= NOW() - INTERVAL '3 months'
)
SELECT 
  signup_cohort,
  COUNT(DISTINCT user_id) as cohort_size,
  SUM(CASE WHEN weeks_since_signup >= 1 AND is_active = 1 THEN 1 ELSE 0 END) as retained_week_1,
  SUM(CASE WHEN weeks_since_signup >= 4 AND is_active = 1 THEN 1 ELSE 0 END) as retained_week_4,
  ROUND(
    SUM(CASE WHEN weeks_since_signup >= 4 AND is_active = 1 THEN 1 ELSE 0 END)::float / 
    COUNT(DISTINCT user_id) * 100, 2
  ) as retention_rate_week_4
FROM cohorts
GROUP BY signup_cohort
ORDER BY signup_cohort DESC;

Пример 8: Классификация по платформам и системам

SELECT 
  user_id,
  CASE 
    WHEN user_agent ILIKE '%iPhone%' OR user_agent ILIKE '%iPad%' THEN 'iOS'
    WHEN user_agent ILIKE '%Android%' THEN 'Android'
    WHEN user_agent ILIKE '%Windows%' THEN 'Windows'
    WHEN user_agent ILIKE '%Mac%' THEN 'macOS'
    ELSE 'Other'
  END as os,
  CASE 
    WHEN user_agent ILIKE '%Chrome%' THEN 'Chrome'
    WHEN user_agent ILIKE '%Safari%' THEN 'Safari'
    WHEN user_agent ILIKE '%Firefox%' THEN 'Firefox'
    WHEN user_agent ILIKE '%Edge%' THEN 'Edge'
    ELSE 'Other'
  END as browser,
  COUNT(*) as sessions
FROM sessions
GROUP BY user_id, os, browser
ORDER BY sessions DESC;

Вложенные CASE

Можно вкладывать CASE друг в друга, хотя это может быть сложно для чтения.

SELECT 
  user_id,
  revenue,
  CASE 
    WHEN revenue >= 1000 THEN
      CASE 
        WHEN created_at >= NOW() - INTERVAL '30 days' THEN 'High-value Active'
        ELSE 'High-value Inactive'
      END
    WHEN revenue >= 500 THEN
      CASE 
        WHEN created_at >= NOW() - INTERVAL '30 days' THEN 'Medium-value Active'
        ELSE 'Medium-value Inactive'
      END
    ELSE
      CASE 
        WHEN created_at >= NOW() - INTERVAL '30 days' THEN 'Low-value Active'
        ELSE 'Low-value Inactive'
      END
  END as segment
FROM users;

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

Ошибка 1: Забыл ELSE

-- Неправильно: NULL для неопределённых случаев
SELECT 
  CASE 
    WHEN age < 18 THEN 'Minor'
    WHEN age < 65 THEN 'Adult'
  END as age_group
FROM users;
-- Люди 65+ получат NULL!

-- Правильно
SELECT 
  CASE 
    WHEN age < 18 THEN 'Minor'
    WHEN age < 65 THEN 'Adult'
    ELSE 'Senior'
  END as age_group
FROM users;

Ошибка 2: CASE с NULL значениями

-- Осторожно с NULL!
SELECT 
  CASE 
    WHEN column_value = NULL THEN 'Empty'  -- НИКОГДА не сработает!
    ELSE 'Not empty'
  END
FROM table;

-- Правильно
SELECT 
  CASE 
    WHEN column_value IS NULL THEN 'Empty'
    ELSE 'Not empty'
  END
FROM table;

Ошибка 3: Порядок условий

-- Неправильно: широкие условия до узких
SELECT 
  CASE 
    WHEN age >= 18 THEN 'Adult'      -- Ловит всех 18+
    WHEN age >= 65 THEN 'Senior'     -- НИКОГДА не срабатывает!
  END
FROM users;

-- Правильно
SELECT 
  CASE 
    WHEN age >= 65 THEN 'Senior'     -- Узкое условие первым
    WHEN age >= 18 THEN 'Adult'      -- Потом более широкое
  END
FROM users;

Performance Tips

  1. Используй CASE в SELECT, а не WHERE: WHERE может быть более сложным
  2. Избегай вложенного CASE: если много уровней, используй VIEW или CTE
  3. Порядок имеет значение: более частые условия первыми
  4. Используй CASE с индексами: CASE может замедлить использование индекса

Чек-лист использования CASE

  • Все возможные случаи обработаны (есть ELSE)
  • Нет условий с NULL без IS NULL
  • Порядок условий логичен (узкие -> широкие)
  • Не используется одно и то же вычисление дважды
  • Понятны все метки категорий
  • Нет вложенности > 2 уровней

CASE WHEN — это инструмент, который позволяет писать чистый и эффективный аналитический SQL.