make_date и make_time собирают значение из готовых чисел: вы передаёте год, месяц, день (или час, минуту, секунду) как отдельные целые, а не парсите строку. Это идеальный инструмент, когда части даты уже лежат в разных колонках отчёта.
Обе функции принимают целые числа и возвращают типизированное значение date или time, сразу проверяя каждую часть на допустимый диапазон. Это снимает два класса проблем разом: не нужно собирать промежуточную строку и угадывать её формат, и не нужно вручную проверять, что месяц лежит в 1..12, а день не выходит за границу месяца. Типичные источники таких разрозненных частей — выгрузки CSV, веб-формы и старые ERP, где год, месяц и день попали в отдельные колонки. Ниже разберём базовый синтаксис, сборку даты из колонок, поведение на некорректных частях и отличия в MySQL и ClickHouse.
Базовый синтаксис
make_date(year, month, day) возвращает date, make_time(hour, min, sec) возвращает time. Аргументы — целые числа (у секунд тип double precision, чтобы хранить доли). Никаких форматных строк вроде 'YYYY-MM-DD' и связанных с ними сюрпризов локали.
SELECT
make_date(2024, 3, 15) AS d,
make_time(14, 30, 0) AS t;
Секунды принимают дробь, поэтому момент с миллисекундами собирается без приведения строк:
SELECT make_time(9, 5, 30.5) AS t;
Полную метку времени даёт make_timestamp(year, month, day, hour, min, sec), а версию с зоной — make_timestamptz(...).
Сборка даты из отдельных колонок
Главный сценарий — таблица, где год, месяц и день хранятся числами в разных полях. Представим, что в orders загрузка из старого источника положила части даты отдельно:
SELECT
order_id,
make_date(y, m, d) AS order_date
FROM order_parts;
make_date снимает боль конкатенации и CAST: не нужно собирать строку y || '-' || m || '-' || d и надеяться, что одиночная цифра месяца не сломает формат. Числа идут в функцию как есть.
Тот же приём удобен для фильтра по дате, собранной из параметров. Все заказы за конкретный день из трёх чисел:
SELECT id, user_id, amount
FROM orders
WHERE created_at::date = make_date(2024, 3, 15)
AND status = 'paid';
Проверка диапазона частей
make_date не молчит при бессмыслице: значение вне допустимого диапазона вызывает ошибку, а не «уезжает» в соседний месяц, как делает арифметика интервалов.
SELECT make_date(2024, 13, 1);
SELECT make_date(2024, 2, 30);
Это и плюс, и подводный камень:
- Плюс: грязные данные (месяц
13, день 0) обнаруживаются сразу, а не превращаются тихо в неверную дату.
- Гочи: один битый ряд в большой выборке роняет весь запрос. Если данные не вычищены, проверяйте части заранее.
Защита от плохих строк через предварительный фильтр:
SELECT order_id, make_date(y, m, d) AS order_date
FROM order_parts
WHERE m BETWEEN 1 AND 12
AND d BETWEEN 1 AND 31;
Отрицательный год трактуется как до нашей эры, поэтому make_date(-1, 1, 1) — это не ошибка, а 0001-01-01 BC. Ноль в году запрещён.
Когда выгоднее make_time
make_time особенно удобен, когда час и минута приходят из настроек или из джойна. Например, нормализуем «рабочее окно» отдела сотрудников к единому времени старта:
SELECT
e.dept,
COUNT(*) AS people,
make_time(9, 0, 0) AS shift_start
FROM employees e
GROUP BY e.dept;
Собранное time можно прибавить к дате и получить полноценный момент без разбора строк:
SELECT
u.id,
make_date(2024, 3, 15) + make_time(18, 0, 0) AS reminder_at
FROM users u
WHERE u.country = 'DE';
Различия в MySQL и ClickHouse
Имена и поведение отличаются от PostgreSQL.
- MySQL функции
make_date в привычном смысле нет. Есть MAKEDATE(year, dayofyear) — она ждёт год и день года, а не месяц с днём. Для сборки из месяца и дня берут STR_TO_DATE, а для времени — MAKETIME(hour, minute, second).
SELECT
STR_TO_DATE('2024-3-15', '%Y-%c-%e') AS d,
MAKETIME(14, 30, 0) AS t;
- ClickHouse предлагает
makeDate(year, month, day) и makeDateTime(year, month, day, hour, minute, second), имена совпадают по смыслу с PostgreSQL.
SELECT
makeDate(2024, 3, 15) AS d,
makeDateTime(2024, 3, 15, 14, 30, 0) AS t;
Главные расхождения при переносе сосредоточены именно в сборке даты из частей. В PostgreSQL make_date(y, m, d) ждёт год, месяц и день и падает с ошибкой на месяце 13 или дне 30 февраля. В MySQL MAKEDATE(year, dayofyear) принимает год и день года, поэтому те же три числа (2024, 3, 15) дадут не 15 марта, а 15-й день 2024 года, то есть 2024-01-15; никакой ошибки при этом не будет. В ClickHouse makeDate(year, month, day) повторяет сигнатуру PostgreSQL, но выход за диапазон не вызывает исключения — значение приводится к границе типа Date. Поэтому при миграции запроса проверяйте не только имя функции, но и порядок и смысл аргументов.
На практике это означает: после переноса прогоните на каждой СУБД одну и ту же тройку чисел с заведомо некорректной частью — например make_date(2024, 13, 1) против makeDate(2024, 13, 1) и MAKEDATE(2024, 13) — и сравните, что вернётся: ошибка, приведённая к границе дата или сдвиг по дню года. Так вы увидите расхождение до того, как его поймает отчёт.
Ключевое отличие при переносе: MySQL MAKEDATE принимает день года, а не месяц и день, и спокойно проглотит то, что PostgreSQL счёл бы ошибкой. Всегда сверяйте сигнатуру, прежде чем переносить запрос между СУБД.
make_dateиmake_timeсобирают значение из готовых чисел: вы передаёте год, месяц, день (или час, минуту, секунду) как отдельные целые, а не парсите строку. Это идеальный инструмент, когда части даты уже лежат в разных колонках отчёта.Обе функции принимают целые числа и возвращают типизированное значение
dateилиtime, сразу проверяя каждую часть на допустимый диапазон. Это снимает два класса проблем разом: не нужно собирать промежуточную строку и угадывать её формат, и не нужно вручную проверять, что месяц лежит в1..12, а день не выходит за границу месяца. Типичные источники таких разрозненных частей — выгрузки CSV, веб-формы и старые ERP, где год, месяц и день попали в отдельные колонки. Ниже разберём базовый синтаксис, сборку даты из колонок, поведение на некорректных частях и отличия в MySQL и ClickHouse.Базовый синтаксис
make_date(year, month, day)возвращаетdate,make_time(hour, min, sec)возвращаетtime. Аргументы — целые числа (у секунд типdouble precision, чтобы хранить доли). Никаких форматных строк вроде'YYYY-MM-DD'и связанных с ними сюрпризов локали.SELECT make_date(2024, 3, 15) AS d, -- 2024-03-15 make_time(14, 30, 0) AS t; -- 14:30:00Секунды принимают дробь, поэтому момент с миллисекундами собирается без приведения строк:
SELECT make_time(9, 5, 30.5) AS t; -- 09:05:30.5Полную метку времени даёт
make_timestamp(year, month, day, hour, min, sec), а версию с зоной —make_timestamptz(...).Сборка даты из отдельных колонок
Главный сценарий — таблица, где год, месяц и день хранятся числами в разных полях. Представим, что в
ordersзагрузка из старого источника положила части даты отдельно:-- order_parts(order_id, y, m, d) SELECT order_id, make_date(y, m, d) AS order_date FROM order_parts;make_dateснимает боль конкатенации иCAST: не нужно собирать строкуy || '-' || m || '-' || dи надеяться, что одиночная цифра месяца не сломает формат. Числа идут в функцию как есть.Тот же приём удобен для фильтра по дате, собранной из параметров. Все заказы за конкретный день из трёх чисел:
SELECT id, user_id, amount FROM orders WHERE created_at::date = make_date(2024, 3, 15) AND status = 'paid';Проверка диапазона частей
make_dateне молчит при бессмыслице: значение вне допустимого диапазона вызывает ошибку, а не «уезжает» в соседний месяц, как делает арифметика интервалов.SELECT make_date(2024, 13, 1); -- ERROR: date field value out of range SELECT make_date(2024, 2, 30); -- ERROR: date field value out of rangeЭто и плюс, и подводный камень:
13, день0) обнаруживаются сразу, а не превращаются тихо в неверную дату.Защита от плохих строк через предварительный фильтр:
SELECT order_id, make_date(y, m, d) AS order_date FROM order_parts WHERE m BETWEEN 1 AND 12 AND d BETWEEN 1 AND 31;Отрицательный год трактуется как до нашей эры, поэтому
make_date(-1, 1, 1)— это не ошибка, а0001-01-01 BC. Ноль в году запрещён.Когда выгоднее make_time
make_timeособенно удобен, когда час и минута приходят из настроек или из джойна. Например, нормализуем «рабочее окно» отдела сотрудников к единому времени старта:SELECT e.dept, COUNT(*) AS people, make_time(9, 0, 0) AS shift_start FROM employees e GROUP BY e.dept;Собранное
timeможно прибавить к дате и получить полноценный момент без разбора строк:SELECT u.id, make_date(2024, 3, 15) + make_time(18, 0, 0) AS reminder_at FROM users u WHERE u.country = 'DE';Различия в MySQL и ClickHouse
Имена и поведение отличаются от PostgreSQL.
make_dateв привычном смысле нет. ЕстьMAKEDATE(year, dayofyear)— она ждёт год и день года, а не месяц с днём. Для сборки из месяца и дня берутSTR_TO_DATE, а для времени —MAKETIME(hour, minute, second).SELECT STR_TO_DATE('2024-3-15', '%Y-%c-%e') AS d, -- 2024-03-15 MAKETIME(14, 30, 0) AS t; -- 14:30:00makeDate(year, month, day)иmakeDateTime(year, month, day, hour, minute, second), имена совпадают по смыслу с PostgreSQL.SELECT makeDate(2024, 3, 15) AS d, makeDateTime(2024, 3, 15, 14, 30, 0) AS t;Главные расхождения при переносе сосредоточены именно в сборке даты из частей. В PostgreSQL
make_date(y, m, d)ждёт год, месяц и день и падает с ошибкой на месяце13или дне30февраля. В MySQLMAKEDATE(year, dayofyear)принимает год и день года, поэтому те же три числа(2024, 3, 15)дадут не 15 марта, а 15-й день 2024 года, то есть2024-01-15; никакой ошибки при этом не будет. В ClickHousemakeDate(year, month, day)повторяет сигнатуру PostgreSQL, но выход за диапазон не вызывает исключения — значение приводится к границе типаDate. Поэтому при миграции запроса проверяйте не только имя функции, но и порядок и смысл аргументов.На практике это означает: после переноса прогоните на каждой СУБД одну и ту же тройку чисел с заведомо некорректной частью — например
make_date(2024, 13, 1)противmakeDate(2024, 13, 1)иMAKEDATE(2024, 13)— и сравните, что вернётся: ошибка, приведённая к границе дата или сдвиг по дню года. Так вы увидите расхождение до того, как его поймает отчёт.Ключевое отличие при переносе: MySQL
MAKEDATEпринимает день года, а не месяц и день, и спокойно проглотит то, что PostgreSQL счёл бы ошибкой. Всегда сверяйте сигнатуру, прежде чем переносить запрос между СУБД.