Анализ проблемы с группировкой по полю DATE в Oracle
Проблемная ситуация
При работе с полем типа DATE
и функцией SYSDATE
возникает неочевидное поведение:
-- Создаем тестовую таблицу
CREATE TABLE xxt_random_data (
random_number NUMBER,
random_string VARCHAR2(100),
creation_date DATE DEFAULT SYSDATE
);
-- Вставка 100 записей (10 итераций × 10 строк)
BEGIN
FOR i IN 1..10 LOOP
INSERT INTO xxt_random_data (random_number, random_string)
SELECT
ROUND(DBMS_RANDOM.VALUE(1, 1000)),
DBMS_RANDOM.STRING('A', 20)
FROM dual
CONNECT BY LEVEL <= 10;
COMMIT;
END LOOP;
END;
/
-- Группировка возвращает 1 запись
select CREATION_DATE, count(*) from xxt_random_data group by CREATION_DATE;
-- Результат:
-- CREATION COUNT(*)
-- -------- ----------
-- 26.06.25 100
Но при увеличении итераций:
-- Вставка 10,000 записей (1000 итераций × 10 строк)
BEGIN
FOR i IN 1..1000 LOOP
INSERT INTO xxt_random_data (random_number, random_string)
SELECT
ROUND(DBMS_RANDOM.VALUE(1, 1000)),
DBMS_RANDOM.STRING('A', 20)
FROM dual
CONNECT BY LEVEL <= 10;
COMMIT;
END LOOP;
END;
/
-- Группировка показывает несколько записей
select CREATION_DATE, count(*) from xxt_random_data group by CREATION_DATE;
-- Результат:
-- CREATION COUNT(*)
-- -------- ----------
-- 26.06.25 2670
-- 26.06.25 1500
-- 26.06.25 2700
-- 26.06.25 310
-- 26.06.25 2820
Причина проблемы
- Тип DATE в Oracle всегда содержит:
- Дату (день, месяц, год)
-
Время (часы, минуты, секунды)
-
SYSDATE возвращает текущие дату и время выполнения операции
-
Отображение в клиенте часто скрывает время, показывая только дату
-
При группировке учитывается полное значение (дата + время)
Решения
1. Использование TRUNC при выборке
SELECT TRUNC(CREATION_DATE), COUNT(*)
FROM xxt_random_data
GROUP BY TRUNC(CREATION_DATE);
2. Использование TRUNC при вставке
INSERT INTO xxt_random_data (random_number, random_string, creation_date)
SELECT
ROUND(DBMS_RANDOM.VALUE(1, 1000)),
DBMS_RANDOM.STRING('A', 20),
TRUNC(SYSDATE) -- Сохраняем только дату
FROM dual;
3. Вынос COMMIT за пределы цикла
BEGIN
FOR i IN 1..1000 LOOP
INSERT INTO xxt_random_data (random_number, random_string)
SELECT ... FROM dual;
END LOOP;
COMMIT; -- Один коммит в конце
END;
/
Вывод
Всегда учитывайте, что тип DATE в Oracle содержит время, даже если оно не отображается. Для работы только с датой используйте TRUNC().