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;  -- убрать время из TIMESTAMP

CAST 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-задаче на деление и проценты. Задачи на приведение типов — в тренажёре Карьерник. Больше вопросов — в разделе с примерами.