TRUNC в Oracle SQL выполняет «усечение» дат и чисел

🟢 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;

Частые ошибки и подводные камни

  1. Индекс и калькуляции слева. Выражения вроде TRUNC(col) в WHERE могут мешать использованию индекса по col. Рассмотрите диапазон: col >= :day AND col < :day + 1.
  2. ‘DAY’ зависит от NLS. TRUNC(d,'DAY') возвращает начало недели по настройкам локали. Для ISO‑недели используйте 'IW'.
  3. DATE vs TIMESTAMP. Уточняйте типы: усечение TIMESTAMP может иметь другой тип результата; при сомнениях приводите к DATE.
  4. Отрицательные разряды у чисел. TRUNC(n,-2) отбрасывает десятки/сотни; для «банковского» округления берите ROUND.
  5. Типографские кавычки. Всегда используйте 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)


🔜 Следующая статья:

DECODE в Oracle SQL — альтернатива CASE для простых условий



Понравилась статья? Поделиться с друзьями: