В любом языке программирования важно знать, как получить текущую дату и время. Обычно это один из самых первых вопросов, возникающих при работе с датами в приложениях. До выхода Oracle8i в PL/SQL существовал только один способ получения даты и времени: функция SYSDATE
. Начиная с Oracle9i, в вашем распоряжении появились все функции из табл. 10.1. От вас лишь требуется понять, как они работают и какие преимущества дает тот или иной вариант.
Функция | Часовой пояс | Тип возвращаемого значения |
CURRENT_DATE | Сеанс | DATE |
CURRENT_TIMESTAMP | Сеанс | TIMESTAMP WITH TIME ZONE |
LOCALTIMESTAMP | Сеанс | TIMESTAMP |
SYSDATE | Сервер базы данных | DATE |
SYSTIMESTAMP | Сервер базы данных | TIMESTAMP WITH TIME ZONE |
Какую же функцию использовать в конкретной ситуации? Ответ зависит от нескольких факторов, которые, вероятно, стоит рассматривать в следующем порядке:
- Если вы используете версию, предшествующую Oracle8i, или должны обеспечить совместимость с ней, выбор небогат: используйте
SYSDATE
. - Какое время вас интересует — вашего сеанса или сервера базы данных? В первом случае используйте функцию, возвращающую сеансовое время, а во втором — функцию, возвращающую часовой пояс базы данных.
- Должен ли часовой пояс возвращаться в составе текущей даты и времени? Если должен, используйте функцию
SYSTIMESTAMP
илиCURRENT_TIMESTAMP
.
Если будет выбрана функция, возвращающая сеансовое время, проследите за тем, чтобы часовой пояс сеанса был задан правильно. Информацию о часовом поясе сеанса и базы данных можно получить при помощи функций SESSIONTIMEZONE
и DBTIMEZONE
соответственно. Чтобы получить время в часовом поясе базы данных, необходимо изменить часовой пояс сеанса на DBTIMEZONE
, а затем использовать одну из сеансовых функций.
Примеры использования этих функций:
BEGIN DBMS_OUTPUT.PUT_LINE('Session Timezone='||SESSIONTIMEZONE); DBMS_OUTPUT.PUT_LINE('Session Timestamp='||CURRENT_TIMESTAMP); DBMS_OUTPUT.PUT_LINE('DB Server Timestamp='||SYSTIMESTAMP); DBMS_OUTPUT.PUT_LINE('DB Timezone='||DBTIMEZONE); EXECUTE IMMEDIATE 'ALTER SESSION SET TIME_ZONE=DBTIMEZONE'; DBMS_OUTPUT.PUT_LINE('DB Timestamp='||CURRENT_TIMESTAMP); -- Возврат часового пояса сеанса к местному значению EXECUTE IMMEDIATE 'ALTER SESSION SET TIME_ZONE=LOCAL'; END; Результат: Session Timezone=-04:00 Session Timestamp=23-JUN-08 12.48.44.656003000 PM -04:00 DB Server Timestamp=23-JUN-08 11.48.44.656106000 AM -05:00 DB Timezone=+00:00 DB Timestamp=23-JUN-08 04.48.44.656396000 PM +00:00
В этом примере сеанс начинается в Восточном часовом поясе (–4:00), тогда как сервер работает по времени Центрального часового пояса (–5:00), но при этом в самой базе данных используется время GMT (+00:00). Чтобы получить время в часовом поясе базы данных, мы сначала приводим часовой пояс сеанса в соответствие с часовым поясом базы данных, а затем вызываем функцию часового пояса сеанса CURRENT_TIMESTAMP
. Наконец, часовой пояс сеанса снова возвращается с исходному местному значению.
Если вы используете эти функции для хронометража на уровне долей секунд, помните об ограничениях операционной системы и оборудования. Функции CURRENT_TIMESTAMP
, LOCALTIMESTAMP
и SYSTIMESTAMP
возвращают значения в типах данных TIMESTAMP WITH TIME ZONE
или TIMESTAMP
, позволяющих определять время с разрешением до миллиардной доли секунды.
Все это, конечно, замечательно, но подумайте, откуда берется это время. База данных получает его от операционной системы в результате вызова GetTimeOfDay (Unix/ Linux), GetSystemTime (Microsoft Windows) или другой аналогичной функции операционной системы. В свою очередь, операционная система зависит от оборудования. Если операционная система или используемое оборудование способны отслеживать время с точностью до сотых долей секунды, база данных не сможет возвращать результаты с большей точностью. Например, в системе Linux на процессоре Intel x86 вы сможете отслеживать время с точностью до миллионной доли секунды (шесть цифр), тогда как при работе базы данных в Microsoft Windows XP или Vista на том же оборудовании обеспечивается точность до тысячной доли секунды. Кроме того, хотя операционная система может возвращать временной штамп с шестью знаками, результат может не соответствовать реальной точности в одну микросекунду.
Что делать, если функции, возвращающей нужный тип данных, не существует — например, если время сервера нужно получить в переменной TIMESTAMP
? Можно доверить неявное преобразование типов базе данных, но лучше воспользоваться явным преобразованием CAST
. Пример:
DECLARE ts1 TIMESTAMP; ts2 TIMESTAMP; BEGIN ts1 := CAST(SYSTIMESTAMP AS TIMESTAMP); ts2 := SYSDATE; DBMS_OUTPUT.PUT_LINE(TO_CHAR(ts1,'DD-MON-YYYY HH:MI:SS AM')); DBMS_OUTPUT.PUT_LINE(TO_CHAR(ts2,'DD-MON-YYYY HH:MI:SS AM')); END; Результат: 24-FEB-2002 06:46:39 PM 24-FEB-2002 06:46:39 PM
Вызов SYSTIMESTAMP
использует CAST
для явного преобразования TIMESTAMP WITH TIME ZONE в TIMESTAMP
. При вызове SYSDATE
преобразование DATE
в TIMESTAMP
выполняется неявно.
ДЛЯ ЧЕГО НУЖНЫ ДВА ТИПА INTERVAL
Поначалу меня несколько удивило то, что Oracle ввела сразу два типа данных INTERVAL
. Решение обрабатывать годы и месяцы отдельно от дней, часов минут и секунд выглядело довольно странно. Почему бы не создать единый тип данных INTERVAL
, покрывающий все возможности? Однако оказалось, что за это нужно благодарить римского императора Юлия Цезаря (создателя юлианского календаря), определившего продолжительность большинства месяцев.
Два типа данных с разделительной линией на уровне месяцев пришлось определить потому, что месяц — это единственный компонент даты/времени с непостоянной продолжительностью. Возьмем интервал в 1 месяц и 30 дней. Какова его фактическая продолжительность? Меньше двух месяцев? Ровно два месяца? А может быть, больше? Если месяцем является январь, то через 30 дней уже наступит март, и получится интервал в 61 день, что уже несколько больше «двух месяцев». Если же этим месяцем является февраль, тогда интервал равен либо 59, либо 60 дням. Ну а если месяц — апрель, тогда интервал составит 60 дней.
Вместо того чтобы возиться со всеми этими сложностями, обусловленными разной продолжительностью месяцев и возникающими при сравнении интервалов, математических операциях с датами, а также при нормализации значений даты/времени, стандарт ISO SQL разбивает модель даты/времени на две части: в одной — год и месяц, во второй — все остальное. За дополнительной информацией по этому вопросу обращайтесь к книге A Guide to the SQL Standard (автор C.J. Date, издательство Addison-Wesley).