CAST в SQL — приведение типов данных
Коротко
CAST — оператор для приведения значения из одного типа данных в другой. Используется для конвертации строк в числа, чисел в строки, строк в даты, целочисленного деления и форматирования вывода. На собеседованиях аналитика CAST встречается в задачах на деление (целочисленное vs дробное), работу с датами и конкатенацию строк с числами.
Синтаксис
-- Стандартный SQL
CAST(expression AS target_type)
-- Короткий синтаксис PostgreSQL
expression::target_type
-- Примеры
SELECT CAST('42' AS INTEGER); -- 42
SELECT CAST(3.14 AS INTEGER); -- 3
SELECT CAST(42 AS VARCHAR); -- '42'
SELECT CAST('2025-03-15' AS DATE); -- 2025-03-15
-- PostgreSQL shorthand
SELECT '42'::INTEGER; -- 42
SELECT 3.14::INTEGER; -- 3
SELECT '2025-03-15'::DATE; -- 2025-03-15:: — синтаксический сахар PostgreSQL, делает то же самое, что CAST. В MySQL и SQL Server используйте CAST или CONVERT.
Частые сценарии
Деление без потери дробной части
Самый частый случай на собеседованиях. Деление двух INTEGER даёт INTEGER:
-- Неправильно: целочисленное деление
SELECT 7 / 2; -- 3 (а не 3.5!)
SELECT COUNT(*) / COUNT(DISTINCT user_id); -- округлит вниз
-- Правильно: приведение к NUMERIC
SELECT CAST(7 AS NUMERIC) / 2; -- 3.5
SELECT 7::NUMERIC / 2; -- 3.5
SELECT COUNT(*)::NUMERIC / COUNT(DISTINCT user_id); -- 3.5
-- Или умножить на 1.0
SELECT 7 * 1.0 / 2; -- 3.5Если хотя бы один операнд — дробный тип (NUMERIC, FLOAT, REAL), результат будет дробным. Приведение одного из операндов достаточно.
Расчёт процента
SELECT
status,
COUNT(*) AS cnt,
ROUND(
COUNT(*)::NUMERIC / SUM(COUNT(*)) OVER () * 100, 1
) AS pct
FROM orders
GROUP BY statusБез ::NUMERIC деление COUNT/SUM вернёт 0 для всех строк, потому что оба — INTEGER.
Строка → число
-- Простое преобразование
SELECT CAST('42' AS INTEGER); -- 42
SELECT CAST('3.14' AS NUMERIC); -- 3.14
-- Невалидная строка — ошибка
SELECT CAST('abc' AS INTEGER); -- ERROR
-- Безопасное преобразование (PostgreSQL)
SELECT CAST('42' AS INTEGER); -- 42
-- Для защиты от ошибок: CASE + regex
SELECT
CASE
WHEN value ~ '^\d+$' THEN CAST(value AS INTEGER)
ELSE NULL
END AS safe_value
FROM raw_dataЧисло → строка (конкатенация)
-- Ошибка: нельзя склеить число и строку напрямую
SELECT 'Заказ #' || order_id FROM orders; -- в PG работает, в MySQL нет
-- Явное приведение — безопаснее
SELECT 'Заказ #' || CAST(order_id AS VARCHAR) FROM orders;
-- Форматирование числа
SELECT TO_CHAR(1234567.89, '9,999,999.99'); -- ' 1,234,567.89'Строка → дата
SELECT CAST('2025-03-15' AS DATE); -- 2025-03-15
SELECT CAST('2025-03-15 14:30:00' AS TIMESTAMP); -- 2025-03-15 14:30:00
-- PostgreSQL
SELECT '2025-03-15'::DATE;
SELECT TO_DATE('15.03.2025', 'DD.MM.YYYY'); -- для нестандартных форматов
-- MySQL
SELECT STR_TO_DATE('15.03.2025', '%d.%m.%Y');Стандартный формат ISO YYYY-MM-DD понимают все СУБД. Для других форматов — TO_DATE (PostgreSQL, Oracle) или STR_TO_DATE (MySQL).
Дата → строка
-- PostgreSQL
SELECT TO_CHAR(NOW(), 'YYYY-MM-DD'); -- '2025-03-15'
SELECT TO_CHAR(NOW(), 'DD.MM.YYYY HH24:MI'); -- '15.03.2025 14:30'
SELECT TO_CHAR(NOW(), 'Month YYYY'); -- 'March 2025'
-- MySQL
SELECT DATE_FORMAT(NOW(), '%Y-%m-%d');
SELECT DATE_FORMAT(NOW(), '%d.%m.%Y');Извлечение компонента даты
-- Год, месяц, день
SELECT EXTRACT(YEAR FROM order_date) FROM orders;
SELECT EXTRACT(MONTH FROM order_date) FROM orders;
SELECT DATE_TRUNC('month', order_date) FROM orders; -- PostgreSQL
-- Или через CAST
SELECT CAST(order_date AS DATE) FROM orders; -- убрать время из TIMESTAMPCAST vs CONVERT
| СУБД | Синтаксис | Пример |
|---|---|---|
| Стандарт SQL | CAST(x AS type) |
CAST('42' AS INT) |
| PostgreSQL | x::type |
'42'::INT |
| MySQL | CAST(x AS type) или CONVERT(x, type) |
CONVERT('42', SIGNED) |
| SQL Server | CAST(x AS type) или CONVERT(type, x, style) |
CONVERT(VARCHAR, GETDATE(), 104) |
CAST — стандартный SQL, работает везде. :: — только PostgreSQL. CONVERT — с разным синтаксисом в MySQL и SQL Server. На собеседовании безопаснее использовать CAST.
Практические примеры
Конверсия в процентах
SELECT
DATE(created_at) AS day,
COUNT(DISTINCT CASE WHEN event = 'purchase' THEN user_id END)::NUMERIC
/ NULLIF(COUNT(DISTINCT CASE WHEN event = 'visit' THEN user_id END), 0)
* 100 AS conversion_pct
FROM events
GROUP BY DATE(created_at)NULLIF защищает от деления на ноль. ::NUMERIC обеспечивает дробный результат.
Округление среднего чека
SELECT
user_id,
ROUND(AVG(amount)::NUMERIC, 2) AS avg_check
FROM orders
GROUP BY user_idВ PostgreSQL ROUND требует NUMERIC, не FLOAT. Приведение через ::NUMERIC — стандартный приём.
Группировка по месяцу из строковой даты
SELECT
TO_CHAR(CAST(date_str AS DATE), 'YYYY-MM') AS month,
COUNT(*) AS events
FROM raw_logs
GROUP BY TO_CHAR(CAST(date_str AS DATE), 'YYYY-MM')
ORDER BY monthТипичные ошибки
Целочисленное деление. SELECT 1 / 3 → 0, не 0.33. Приведите хотя бы один операнд: 1::NUMERIC / 3 → 0.33.
Невалидное преобразование. CAST('abc' AS INTEGER) — ошибка. Проверяйте данные перед CAST или используйте CASE + regex.
Потеря точности. CAST(3.999 AS INTEGER) → 3 (не 4). CAST обрезает, не округляет. Для округления — ROUND(3.999) → 4.
Неявное приведение. PostgreSQL автоматически приведёт '42' к числу в контексте сравнения: WHERE age > '25' сработает. Но полагаться на это — плохая практика. Явный CAST безопаснее.
Вопросы с собеседований
-- Что вернёт SELECT 5 / 2?
-- В PostgreSQL и MySQL — 2 (целочисленное деление). Для дробного результата: 5::NUMERIC / 2 или 5 / 2.0 → 2.5.
-- Как посчитать процент от общего в SQL?
-- COUNT(*)::NUMERIC / SUM(COUNT(*)) OVER () * 100. Ключевой момент — приведение к NUMERIC для дробного деления.
-- Чем отличается CAST от ::?
-- :: — синтаксис PostgreSQL, шорткат для CAST. CAST(x AS type) — стандартный SQL, работает во всех СУБД.
-- Как безопасно привести строку к числу?
-- Проверка через CASE: CASE WHEN col ~ '^\d+$' THEN col::INT ELSE NULL END. В PostgreSQL 16+ есть TRY_CAST (аналог SQL Server).
Потренируйтесь решать задачи — откройте тренажёр с 1500+ вопросами для подготовки к собеседованиям аналитиков.
FAQ
CAST округляет или обрезает?
Обрезает (truncate). CAST(3.9 AS INT) → 3, не 4. Для округления используйте ROUND(3.9) → 4. Это частый подвох на собеседованиях.
Можно ли CAST NULL?
Да. CAST(NULL AS INTEGER) вернёт NULL типа INTEGER. Это полезно в UNION, где столбцы должны иметь одинаковый тип.
CAST vs TO_CHAR / TO_DATE?
CAST — универсальный, но не позволяет указать формат. TO_CHAR / TO_DATE / TO_NUMBER (PostgreSQL, Oracle) — с указанием формата. Для нестандартных форматов дат — TO_DATE.
Как тренироваться
CAST встречается в каждой второй SQL-задаче на деление и проценты. Задачи на приведение типов — в тренажёре Карьерник. Больше вопросов — в разделе с примерами.