TRUNC в Oracle SQL. Введение
TRUNC в Oracle SQL выполняет «усечение» значений: для чисел убирает дробную часть (или разряды), для дат/времени — обрезает до указанной единицы (день, месяц, год, час, минута и т. д.).
Это один из самых используемых инструментов для отчётов, агрегаций и фильтрации по датам без учёта времени.
Где используют
- Фильтры по дате: сравнение «по дню/месяцу» без влияния времени.
- Группировки: свод по дням, неделям, месяцам, кварталам.
- Агрегации: «псевдо‑пивоты» и счётчики по периодам.
- Числовые округления вниз: отбрасывание дроби или разрядов (десятки, сотни).
Синтаксис
Числа:
TRUNC(n [, decimals])n— число или выражение;decimals— опционально, сколько знаков после запятой оставить (усечь). Может быть отрицательным (десятки, сотни).
Дата/время:
TRUNC(d [, fmt])d— DATE/TIMESTAMP/выражение;fmt— опционально, единица усечения: ‘YYYY’, ‘Q’, ‘MONTH’/’MM’, ‘IW’/’WW’, ‘DDD’/’DD’, ‘DAY’/’D’, ‘HH’/’HH24’, ‘MI’ и др.
Примечание: TRUNC(d) без формата усечёт до начала дня (00:00:00).
100 примеров
1. Срез по дню от SYSDATE
SELECT
TRUNC(SYSDATE) AS day_start
FROM dual;2. Начало месяца от SYSDATE (‘MM’)
SELECT
TRUNC(SYSDATE, 'MM') AS month_start
FROM dual;3. Старт года (‘YYYY’)
SELECT
TRUNC(SYSDATE, 'YYYY') AS year_start
FROM dual;4. Старт квартала (‘Q‘)
SELECT
TRUNC(SYSDATE, 'Q') AS quarter_start
FROM dual;5. Старт ISO‑недели (‘IW‘)
SELECT
TRUNC(SYSDATE, 'IW') AS iso_week_start
FROM dual;6. Неделя по локали (‘DAY‘)
SELECT
TRUNC(SYSDATE, 'DAY') AS nls_week_start
FROM dual;7. Неделя года (‘WW‘)
SELECT
TRUNC(SYSDATE, 'WW') AS week_of_year_start
FROM dual;8. Начало суток без формата
SELECT
TRUNC(SYSDATE) AS day_zero_time
FROM dual;9. Обрезка до часа (‘HH24‘)
SELECT
TRUNC(SYSDATE, 'HH24') AS hour_start
FROM dual;10. Обрезка до минуты (‘MI‘)
SELECT
TRUNC(SYSDATE, 'MI') AS minute_start
FROM dual;11. Месячный срез для произвольной DATE
SELECT
TRUNC(TO_DATE('2025-09-13','YYYY-MM-DD'), 'MM') AS month_start
FROM dual;12. TIMESTAMP до начала дня
SELECT
TRUNC(SYSTIMESTAMP) AS ts_day_start
FROM dual;13. TIMESTAMP до часа (‘HH24‘)
SELECT
TRUNC(SYSTIMESTAMP, 'HH24') AS ts_hour_start
FROM dual;14. Перевод строки в дату и срез до месяца
SELECT
TRUNC(TO_DATE('2024-02-22','YYYY-MM-DD'), 'MM') AS month_start
FROM dual;15. Квартальный старт из произвольной даты
SELECT
TRUNC(TO_DATE('2025-01-15','YYYY-MM-DD'), 'Q') AS quarter_start
FROM dual;16. Годовой срез от CURRENT_DATE
SELECT
TRUNC(CURRENT_DATE, 'YYYY') AS year_start
FROM dual;17. Месячный срез от CURRENT_TIMESTAMP
SELECT
TRUNC(CURRENT_TIMESTAMP, 'MM') AS month_start
FROM dual;18. ISO‑понедельник от CURRENT_DATE
SELECT
TRUNC(CURRENT_DATE, 'IW') AS iso_week_start
FROM dual;19. Неделя по локали от CURRENT_DATE
SELECT
TRUNC(CURRENT_DATE, 'DAY') AS nls_week_start
FROM dual;20. Начало суток от CURRENT_DATE
SELECT
TRUNC(CURRENT_DATE) AS day_start
FROM dual;Еще 20 примеров.
Еще 20 примеров.
21. Свод по дням (GROUP BY по началу суток)
SELECT
TRUNC(order_date) AS day_key,
COUNT(*) AS orders_cnt
FROM orders
GROUP BY TRUNC(order_date)
ORDER BY day_key;22. Свод по месяцам (ключ — первый день месяца)
SELECT
TRUNC(order_date, 'MM') AS month_key,
SUM(amount) AS total_amount
FROM orders
GROUP BY TRUNC(order_date, 'MM')
ORDER BY month_key;23. Свод по ISO‑неделям
SELECT
TRUNC(order_date, 'IW') AS iso_week_key,
COUNT(*) AS orders_cnt
FROM orders
GROUP BY TRUNC(order_date, 'IW')
ORDER BY iso_week_key;24. Фильтр: только за конкретный день (через равенство)
SELECT
*
FROM orders
WHERE TRUNC(order_date) = TRUNC(TO_DATE(:p_date, 'YYYY-MM-DD'));25. Фильтр по месяцу через ключ ‘YYYY-MM-01’
SELECT
*
FROM orders
WHERE TRUNC(order_date, 'MM') = DATE '2025-09-01';26. Фильтр диапазоном (индекс‑дружественный)
SELECT
*
FROM orders
WHERE order_date >= DATE '2025-09-01'
AND order_date < DATE '2025-09-02';27. BETWEEN для всего месяца (через границы)
SELECT
*
FROM orders
WHERE order_date BETWEEN DATE '2025-09-01' AND DATE '2025-09-30' + 0.99999;28. Конец месяца через LAST_DAY
SELECT
LAST_DAY(TRUNC(SYSDATE, 'MM')) AS month_end
FROM dual;29. Нормализация даты перед JOIN
SELECT
o.customer_id,
TRUNC(o.order_date) AS day_key,
SUM(o.amount) AS total
FROM orders o
JOIN calendars c
ON TRUNC(o.order_date) = c.day_key
GROUP BY o.customer_id, TRUNC(o.order_date);30. Фильтр за текущий месяц
SELECT
*
FROM orders
WHERE order_date >= TRUNC(SYSDATE, 'MM')
AND order_date < ADD_MONTHS(TRUNC(SYSDATE, 'MM'), 1);31. Сравнение с EXTRACT(YEAR FROM …)
SELECT
*
FROM orders
WHERE EXTRACT(YEAR FROM order_date) = 2025;32. Группировка по году и месяцу через EXTRACT
SELECT
EXTRACT(YEAR FROM order_date) AS y,
EXTRACT(MONTH FROM order_date) AS m,
SUM(amount) AS total
FROM orders
GROUP BY EXTRACT(YEAR FROM order_date), EXTRACT(MONTH FROM order_date);33. Отчёт по ISO‑неделям (‘IW‘)
SELECT
TRUNC(order_date, 'IW') AS iso_week_key,
SUM(amount) AS total
FROM orders
GROUP BY TRUNC(order_date, 'IW');34. Отчёт по локальным неделям (‘DAY‘)
SELECT
TRUNC(order_date, 'DAY') AS nls_week_key,
COUNT(*) AS cnt
FROM orders
GROUP BY TRUNC(order_date, 'DAY');35. Границы соседних месяцев
SELECT
TRUNC(ADD_MONTHS(SYSDATE, -1), 'MM') AS prev_month_start,
TRUNC(SYSDATE, 'MM') AS this_month_start
FROM dual;36. Квартальный ключ из произвольной даты
SELECT
TRUNC(TO_DATE(:d, 'YYYY-MM-DD'), 'Q') AS quarter_start
FROM dual;37. Стабильная сортировка по дням
SELECT
order_id, order_date
FROM orders
ORDER BY TRUNC(order_date), order_id;38. HAVING после группировки по датам
SELECT
TRUNC(order_date) AS day_key,
COUNT(*) AS cnt
FROM orders
GROUP BY TRUNC(order_date)
HAVING COUNT(*) > 100;39. Ключ часа для событий
SELECT
TRUNC(event_ts, 'HH24') AS hour_key,
COUNT(*) AS cnt
FROM events
GROUP BY TRUNC(event_ts, 'HH24')
ORDER BY hour_key;Еще 20 примеров.
40. Ключ минуты для событий
SELECT
TRUNC(event_ts, 'MI') AS minute_key,
COUNT(*) AS cnt
FROM events
GROUP BY TRUNC(event_ts, 'MI')
ORDER BY minute_key;Еще 20 примеров.
41. Усекаем 123.456 до целого
SELECT
TRUNC(123.456) AS n
FROM dual;42. Усекаем отрицательное значение
SELECT
TRUNC(-123.456) AS n
FROM dual;43. Два знака после запятой
SELECT
TRUNC(123.456, 2) AS n
FROM dual;44. До десятков (decimals = -1)
SELECT
TRUNC(123.456, -1) AS n
FROM dual;45. До сотен (decimals = -2)
SELECT
TRUNC(9876.5, -2) AS n
FROM dual;46. Денежные суммы: сохраняем 2 знака
SELECT
order_id,
TRUNC(amount, 2) AS amount_trunc2
FROM orders;47. Себестоимость × количество с усечением
SELECT
item_id,
TRUNC(unit_price * qty, 2) AS total_cost_trunc
FROM items;48. Отбрасываем копейки
SELECT
customer_id,
TRUNC(total_amount, 0) AS rubles_only
FROM invoices;49. Инженерные разряды (тысячи)
SELECT
metric_id,
TRUNC(metric, -3) AS rounded_down_to_thousands
FROM metrics;50. Сравнение с ROUND для числа
SELECT
TRUNC(12.56, 1) AS trunc1,
ROUND(12.56, 1) AS round1
FROM dual;51. Разбиение на диапазоны значений
SELECT
TRUNC(value, -1) AS band,
COUNT(*) AS cnt
FROM numbers
GROUP BY TRUNC(value, -1)
ORDER BY band;52. «Минус ноль» в Oracle — обычный ноль
SELECT
TRUNC(-0.75) AS n
FROM dual;53. Представление с ценами до 2 знаков
CREATE OR REPLACE VIEW v_prices AS
SELECT
product_id,
TRUNC(price, 2) AS price2
FROM products;54. Итоговая цена с учётом скидки
SELECT
product_id,
TRUNC(list_price * (1 - discount), 2) AS net_price
FROM products;55. Оконная сумма по часам (PARTITION BY ‘HH24‘)
SELECT
SUM(val) OVER (PARTITION BY TRUNC(ts, 'HH24')) AS hour_sum,
ts, val
FROM stream;56. Дневная оконная сумма
SELECT
TRUNC(ts) AS day_key,
SUM(val) OVER (PARTITION BY TRUNC(ts)) AS day_sum
FROM stream;57. Фильтр: текущие сутки (границы дня)
SELECT
*
FROM events
WHERE event_ts >= TRUNC(SYSDATE)
AND event_ts < TRUNC(SYSDATE) + 1;58. Фильтр: текущий месяц (границы месяца)
SELECT
*
FROM events
WHERE event_ts >= TRUNC(SYSDATE, 'MM')
AND event_ts < ADD_MONTHS(TRUNC(SYSDATE, 'MM'), 1);Еще 20 примеров.
59. Границы года (включая последнюю секунду)
SELECT
TRUNC(SYSDATE, 'YYYY') AS y_start,
ADD_MONTHS(TRUNC(SYSDATE, 'YYYY'), 12) - 1/86400 AS y_end_inclusive
FROM dual;60. Сдвиг на первый день смещённого месяца
SELECT
TRUNC(ADD_MONTHS(SYSDATE, :m), 'MM') AS shifted_month_start
FROM dual;Еще 20 примеров.
61. Первый рабочий день месяца (условная логика)
SELECT
CASE
WHEN TO_CHAR(TRUNC(SYSDATE,'MM'), 'DY', 'NLS_DATE_LANGUAGE=ENGLISH') IN ('SAT','SUN')
THEN NEXT_DAY(TRUNC(SYSDATE,'MM'), 'MONDAY')
ELSE TRUNC(SYSDATE,'MM')
END AS first_workday
FROM dual;62. Последний день месяца
SELECT
LAST_DAY(SYSDATE) AS month_end
FROM dual;63. Конец месяца как метка времени
SELECT
CAST(LAST_DAY(SYSDATE) + (86399/86400) AS DATE) AS month_end_ts
FROM dual;64. Понедельник ISO‑недели для произвольной даты
SELECT
TRUNC(TO_DATE(:d,'YYYY-MM-DD'), 'IW') AS week_monday
FROM dual;65. Начало недели по локали для произвольной даты
SELECT
TRUNC(TO_DATE(:d,'YYYY-MM-DD'), 'DAY') AS nls_week_start
FROM dual;66. Нулевые минуты у SYSTIMESTAMP (час)
SELECT
TRUNC(SYSTIMESTAMP, 'HH24') AS hour_start
FROM dual;67. Нулевые секунды у SYSTIMESTAMP (минута)
SELECT
TRUNC(SYSTIMESTAMP, 'MI') AS minute_start
FROM dual;68. Юлианский день (‘J‘)
SELECT
TRUNC(TO_DATE('2025-09-13','YYYY-MM-DD'), 'J') AS julian_day_start
FROM dual;69. Номер «декады» месяца (через арифметику)
SELECT
TRUNC(TO_NUMBER(TO_CHAR(SYSDATE,'DD')) / 10) * 10 AS day_decade
FROM dual;70. Перевод в UTC и срез по дню
SELECT
TRUNC(CAST(CURRENT_TIMESTAMP AT TIME ZONE 'UTC' AS DATE)) AS utc_day_start
FROM dual;71. Ключ дня для фактов
SELECT
fact_id,
TRUNC(event_date) AS day_key
FROM fact_table;72. Ключ месяца для витрины
SELECT
TRUNC(event_date, 'MM') AS month_key,
SUM(amount) AS total
FROM fact_table
GROUP BY TRUNC(event_date, 'MM');73. Ключ часа для витрины событий
SELECT
TRUNC(ts, 'HH24') AS hour_key,
COUNT(*) AS cnt
FROM events
GROUP BY TRUNC(ts, 'HH24');74. Диапазон часа (полуинтервал)
SELECT
*
FROM events
WHERE ts >= TRUNC(ts, 'HH24')
AND ts < TRUNC(ts, 'HH24') + 1/24;75. Диапазон минуты (полуинтервал)
SELECT
*
FROM events
WHERE ts >= TRUNC(ts, 'MI')
AND ts < TRUNC(ts, 'MI') + 1/1440;76. Сравнение двух дат до дня
SELECT
*
FROM dual
WHERE TRUNC(TO_DATE(:a,'YYYY-MM-DD')) = TRUNC(TO_DATE(:b,'YYYY-MM-DD'));77. Нормализация временных меток в MERGE
MERGE INTO tgt t
USING (
SELECT id, TRUNC(ts) AS day_key, value FROM src
) s
ON (t.id = s.id AND t.day_key = s.day_key)
WHEN MATCHED THEN UPDATE SET t.value = s.value
WHEN NOT MATCHED THEN INSERT (id, day_key, value) VALUES (s.id, s.day_key, s.value);Еще 20 примеров.
78. NULL‑безопасная нормализация дат
SELECT
CASE WHEN dt IS NULL THEN NULL ELSE TRUNC(dt) END AS day_key
FROM t;79. Суточные максимумы
SELECT
TRUNC(ts) AS day_key,
MAX(val) AS day_max
FROM measures
GROUP BY TRUNC(ts);80. Месячные суммы
SELECT
TRUNC(ts, 'MM') AS month_key,
SUM(val) AS month_sum
FROM measures
GROUP BY TRUNC(ts, 'MM');Еще 20 примеров.
81. Квартальный ключ и сумма по кварталу
SELECT
TRUNC(order_date, 'Q') AS quarter_key,
SUM(amount) AS total
FROM orders
GROUP BY TRUNC(order_date, 'Q');82. Прошлый месяц (границы периода)
SELECT
*
FROM orders
WHERE order_date >= TRUNC(ADD_MONTHS(SYSDATE, -1), 'MM')
AND order_date < TRUNC(SYSDATE, 'MM');83. Границы текущего квартала
SELECT
TRUNC(SYSDATE, 'Q') AS q_start,
ADD_MONTHS(TRUNC(SYSDATE, 'Q'), 3) - 1/86400 AS q_end
FROM dual;84. Начало следующей ISO‑недели
SELECT
ADD_MONTHS(TRUNC(SYSDATE, 'IW'), 0) + 7 AS next_iso_week_start
FROM dual;85. Сравнение начала ‘WW‘ и ‘IW‘
SELECT
TRUNC(SYSDATE, 'WW') AS ww_start,
TRUNC(SYSDATE, 'IW') AS iw_start
FROM dual;86. Разница в днях между двумя датами
SELECT
TRUNC(end_date) - TRUNC(start_date) AS days_diff
FROM periods;87. Сравнение с ROUND для дат
SELECT
TRUNC(TO_DATE('2025-01-16','YYYY-MM-DD'),'MM') AS trunc_month,
ROUND(TO_DATE('2025-01-16','YYYY-MM-DD'),'MM') AS round_month
FROM dual;88. Ключ вида ‘YYYY-MM’ для отчёта
SELECT
TO_CHAR(TRUNC(order_date,'MM'),'YYYY-MM') AS ym_key,
COUNT(*) AS cnt
FROM orders
GROUP BY TRUNC(order_date,'MM');89. ТОП‑дни продаж: сортировка по сумме
SELECT
TRUNC(order_date) AS day_key,
SUM(amount) AS total
FROM orders
GROUP BY TRUNC(order_date)
ORDER BY total DESC
FETCH FIRST 10 ROWS ONLY;90. Накопительная сумма по дням
SELECT
TRUNC(order_date) AS day_key,
SUM(amount) AS total,
SUM(SUM(amount)) OVER (ORDER BY TRUNC(order_date)) AS running_total
FROM orders
GROUP BY TRUNC(order_date);91. Почасовой мониторинг нагрузки
SELECT
TRUNC(event_ts,'HH24') AS hour_key,
COUNT(*) AS events
FROM events
GROUP BY TRUNC(event_ts,'HH24')
ORDER BY hour_key;92. Поминутный мониторинг задержек
SELECT
TRUNC(event_ts,'MI') AS minute_key,
AVG(latency_ms) AS avg_ms
FROM events
GROUP BY TRUNC(event_ts,'MI');93. Суточные пики (HAVING)
SELECT
TRUNC(ts) AS day_key,
COUNT(*) AS cnt
FROM events
GROUP BY TRUNC(ts)
HAVING COUNT(*) > 1000;94. Средние значения по месяцам
SELECT
TRUNC(ts,'MM') AS month_key,
AVG(val) AS avg_val
FROM measures
GROUP BY TRUNC(ts,'MM');95. Медиана по кварталам (PERCENTILE_CONT)
SELECT
TRUNC(ts,'Q') AS quarter_key,
PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY val) AS median_val
FROM measures
GROUP BY TRUNC(ts,'Q');96. Приведение TIMESTAMP к DATE перед усечением
SELECT
TRUNC(CAST(ts AS DATE)) AS day_key
FROM events;97. ISO‑неделя и её номер
SELECT
TRUNC(ts,'IW') AS week_key,
TO_CHAR(ts,'IW') AS week_no
FROM events;98. Ключ формата ‘YYYY-MM-DD’
SELECT
TO_CHAR(TRUNC(ts),'YYYY-MM-DD') AS day_key,
COUNT(*) AS cnt
FROM events
GROUP BY TRUNC(ts);99. Начало следующего месяца
SELECT
TRUNC(ts,'MM') AS month_key,
ADD_MONTHS(TRUNC(ts,'MM'),1) AS next_month_start
FROM events;100. Универсальный фильтр по дню (полуинтервал)
SELECT
*
FROM t
WHERE dt >= TRUNC(TO_DATE(:d,'YYYY-MM-DD'))
AND dt < TRUNC(TO_DATE(:d,'YYYY-MM-DD')) + 1;Частые ошибки и подводные камни
- Индекс и калькуляции слева. Выражения вроде
TRUNC(col)вWHEREмогут мешать использованию индекса поcol. Рассмотрите диапазон:col >= :day AND col < :day + 1. - ‘DAY’ зависит от NLS.
TRUNC(d,'DAY')возвращает начало недели по настройкам локали. Для ISO‑недели используйте'IW'. - DATE vs TIMESTAMP. Уточняйте типы: усечение
TIMESTAMPможет иметь другой тип результата; при сомнениях приводите кDATE. - Отрицательные разряды у чисел.
TRUNC(n,-2)отбрасывает десятки/сотни; для «банковского» округления берите ROUND. - Типографские кавычки. Всегда используйте ASCII‑кавычки в форматах: ‘YYYY’, ‘MM’, ‘IW’.
TRUNC vs ROUND
- TRUNC — отбрасывает дробь/разряды, «в сторону нуля» для чисел; для дат — срез до начала периода.
- ROUND — округляет по правилам математики; для дат — до «ближайшего» периода (например, середина месяца округлится к следующему).
Заключение
TRUNC — незаменим для календарной аналитики и аккуратного подсчёта по периодам.
Комбинируйте его с GROUP BY, датными функциями и аккуратными предикатами в WHERE, чтобы получать быстрые и точные отчёты.
Документация
Oracle SQL Language Reference — TRUNC (date)
Oracle SQL Language Reference — TRUNC (number)