Migrating Oracle DATE columns to TIMESTAMP with timezone - sql

Bakground:
I've got a legacy app I'm working on that uses DATE types for most time storage in the database. I'd like to try update some of these tables so that they can utilize time zones since this is causing problems with users in different areas from where the db is(see A below). This is for Oracle 10g.
Quetions:
1) Can I migrate this "in place." That is can I convert like so
DATE_COL = type:DATE => DATE_COL = type:TIMESTAMP
...or will I have to use a different column name?
Keep in mind that data needs to be retained. If this can be done semi-easily in a migration script it will work for my purposes.
2) Will this type of conversion be backwards compatible? We likely have some scripts or reports that will hit this table that we may not know about. We can probably deal with it but I'd like to know what sort of hornet's nest I'm walking into.
3) What pitfalls should I be on the lookout for?
Thanks,
EDIT:
(partly in response to Gary)
I'm fine with a multi-step process.
1) move data to a new Timestamp column (caled TEMP) with some sort of conversion
2) drop old column (we'll call it MY_DATE)
3) create new timestamp column with the old date column name (MY_DATE)
4) move data to the MY_DATE column
5) drop TEMP column
A Gary also wanted clarification on the specific timezone issue. I copied my answer from below to keep it more readable.
Basically the data will be accessed from several different areas. We need to be able to convert to/from the local time zone as needed. We also have triggers that use sysdate further complicating things. timestamp with time zone alleviates a lot of this pain.
Oh and thanks for the answers so far.

You could just run:
ALTER TABLE your_table MODIFY your_date_column TIMESTAMP WITH TIME ZONE;
But I would recommend adding a TIMESTAMP column to the table, using an UPDATE statement to populate, and drop the original date column if you so choose:
ALTER TABLE your_table ADD date_as_timestamp TIMESTAMP WITH TIME ZONE;
UPDATE your_table
SET date_as_timestamp = CAST(date_column AS TIMESTAMP WITH TIME ZONE);
The conversion is backwards compatible - you can switch back & forth as you like.

Simple enough to demonstrate
SQL> create table x (y date);
Table created.
SQL> insert into x select sysdate from dual;
1 row created.
SQL> commit;
Commit complete.
SQL> alter table x modify y timestamp;
Table altered.
SQL> select * from x;
Y
---------------------------------------------------------------------------
03/NOV/09 12:49:03.000000 PM
SQL> alter table x modify y date;
Table altered.
SQL> select * from x;
Y
---------
03/NOV/09
SQL> alter table x modify y timestamp with time zone;
alter table x modify y timestamp with time zone
ERROR at line 1:
ORA-01439: column to be modified must be empty to change datatype
SQL> alter table x modify y timestamp with local time zone;
Table altered.
SQL> alter table x modify y date;
Table altered.
So you can go from date to timestamp (or timestamp with local timezone) and back again, but not for timestamp with time zone (ie where the offset is persisted).
You'd have to add another column, and copy the existing data over (with a default for the appropriate time zone).
"causing problems with users in different areas from where the db is".
Might help to be a bit more specific. Is it sufficient to convert the dates (or timestamps) from the database timezone to the user's timezone when inserted/changed/queried, or do you actually need to persist the fact that the record was created at 3:00pm in a specific timezone.

Related

How to split a datetime column in to two columns of Date and time separately in Oracle SQL?

I have a datetime column.
I want two columns: a date and a time column.
How can I split my column into two?
Use:
a DATE data-type with the time component set to midnight for the date (you can enforce this with a check constraint); and
an INTERVAL DAY(0) TO SECOND data-type for the time component.
CREATE TABLE table_name(
datetime_column DATE,
date_column DATE,
time_column INTERVAL DAY(0) TO SECOND,
CONSTRAINT table_name__date_column__chk CHECK (date_column = TRUNC(date_column))
)
If you want to get the combined date-time then you can easily add the two to get back to a date-time value.
How can I split my column into two?
Assuming you have the columns you can use:
UPDATE table_name
SET date_column = TRUNC(datetime_column),
time_column = (datetime_column - TRUNC(datetime_column)) DAY TO SECOND;
db<>fiddle here
As Gordon commented, there's no time datatype in Oracle.
Though, literally answering what you asked, you can separate date and time and store each of them into their own columns - it's just that these will be VARCHAR2 columns and you can only look at how pretty they are. You can't, for example, do any date arithmetic on them; first you'd have to convert them back to date datatype, so question is what you really want to do with what you get.
Anyway, here you are:
SQL> create table test
2 (datum date,
3 date_only varchar2(10),
4 time_only varchar2(8)
5 );
Table created.
Sample value:
SQL> insert into test (datum) values (sysdate);
1 row created.
Split date to two parts:
SQL> update test set
2 date_only = to_char(datum, 'dd.mm.yyyy'),
3 time_only = to_char(datum, 'hh24:mi:ss');
1 row updated.
What's in there?
SQL> alter session set nls_date_format = 'dd.mm.yyyy hh24:mi:ss';
Session altered.
SQL> select * from test;
DATUM DATE_ONLY TIME_ONL
------------------- ---------- --------
05.08.2021 21:05:06 05.08.2021 21:05:06
SQL>
Since there is no specific datatype for time, here my suggestion would be to keep the datetime in main column and add two VIRTUAL COLUMN for date value and time value respectively.
Oracle 11g has introduced a new feature that allows you to create a VIRTUAL COLUMN, an empty column that contains a function upon other table columns (the function itself is stored in the data dictionary).
However, it all depends on what you are going to do with it.
Please elaborate your requirement so that you will get a more specific answer.

Oracle 10 SQL - How to use a "00:00" time format in a constraint

Small entry level question with Oracle 10 SQL. I'm creating a table with a column with a "date" type which is supposed to hold values looking like this : "00:00". I have a constraint with checks the time to be between 00:00 and 23:00.
Now, what I can't quite grasp is how to approach the problem. I do feel like I'm missing something quite basic but I can't quite figure out what...
Do I :
1) Extract and check the date inside my constraint? If so, is there a way to do that? Can I insert data looking like this : TO_DATE('13-AUG-66 12:56','DD-MON-YY HH:MI'), and use some kind of "Extract" function inside my constraint?
2) The exercise in question does mention the date type for that particular column. By default, I assume that it doesn't hold hours and needs to be modified using alter_session?
A constraint only enforces a restriction. It cannot modify data. A BEFORE INSERT trigger can modify data but is generally less efficient than a constraint.
If you want to create a constraint that ensures that the time component is always midnight
CREATE TABLE table_name (
col DATE CHECK( col1 = TRUNC( col ))
);
If you want to create a trigger that modifies the data
CREATE OR REPLACE TRIGGER trg_trunc_dt
BEFORE INSERT ON table_name
FOR EACH ROW
BEGIN
:new.date_column := TRUNC( :new.date_column );
END;
A DATE always contains a day and a time component. Your client may or may not display either component. Many clients will use implicit data type conversion in which case the session's NLS_DATE_FORMAT controls how a DATE is converted to a VARCHAR2 and what elements are incorporated into the string.
A date type always has a date part and a time part. It is just a value and has thus no formatting. If you display a time as 22:50 or 10:50pm for example is up to you. You either rely on your settings with to_char(mydate) or specify a format to_char(mydate,'hh24:mi').
This said, you can simply use the time part of your column and ignore the date part. If you want to avoid confusion about different dates being stored, you can use a trigger setting the date part to 01.01.0001 for instance:
create or replace Trigger trg_datetable_datepart
before insert or update of mydate on datetable
for each row
begin
:new.mydate := to_date( '01.01.0001 ' || to_char(:new.mydate, 'hh24:mi') , 'dd.mm.yyyy hh24:mi' );
end;
To avoid inserts of times after 23h you would write a check constraint:
alter table datetable add constraint check_datetable_timepart check ( to_char(mydate, 'hh24:mi') <= '23:00' );

Update Oracle timestamp to current date

I have a TIMESTAMP(6) field in Oracle db. Value of this field is in format
DD/MM/YYYY HH:MM:SS.000000000 PM
How to update this value to the current timestamp?
[a link to a similar question:] update date value in oracle
I followed this link, but following query is taking very long time to execute.
update table_name set start_time = to_char(to_date(start_time, 'yyyy/mm/dd-hh:mi:ss:ff3'), '2012/10/10-19:30:00:00') where column='Q'
A timestamp is a point in time, it has no format. To update such a field to the current timestamp, use SYSTIMESTAMP or CURRENT_TIMESTAMP (respectively the date/time of the server and the date/time of the session):
UPDATE your_table
SET your_column = systimestamp
WHERE ...
If the query takes an abnormal amount of time (much longer than a comparable SELECT with the same WHERE clause), the mostly likely causes are:
The rows that your are updating are locked by another session (doing a SELECT FOR UPDATE NOWAIT on these rows will make sure that you have the lock).
You have triggers that perform additional work,
You're updating a column referenced by a non-indexed foreign key.
Why you don't just
update table_name
set start_date = systimestamp
where column='Q'
If you suspect there are locks on the table, there are some tables to check: dba_locks, v$session, v$session_blockers etc. These are useful when a user blocked something with an accidental update without a commit or rollback, but you should be able to see if can exists blocking locks from the architecture of your application. You should just simulate on paper all the scenarios.

Is it possible to get last update time of the row in sql

Is it possible to get last updated time and date of the row using MYSQL server.
Well there is no inbuild feature exists with MySQL. Though you can get the same effect by adding a timestamp column:
ALTER TABLE NAMEYOURTABLE
ADD COLUMN last_update TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
using above to create timestamp with name last_update column will make it pretty much automatically managed and updated. Now you can select from NAMEYOURTABLE the last updated row based on the timestamp.

mysql automatically store record creation timestamp

Is there some way mysql can store timestamp automatically in a record row whenever that it is created. I was trying to use timestamp(data type) with current_timestamp as default value but then realised this will get updated everytime the record is updated. I just need something that will store create timestamp.
Thanks
Set the DEFAULT constraint to use CURRENT_TIMESTAMP:
CREATE TABLE ...
your_date_column DATETIME DEFAULT CURRENT_TIMESTAMP
...
For an existing table, use the ALTER TABLE statement:
ALTER TABLE your_table
ALTER COLUMN date_column SET DEFAULT CURRENT_TIMESTAMP
Unless you specify a value to for the date_column, the default will be the date & time the INSERT statement was run. NULL and DEFAULT or valid values to use the default constraint otherwise, assuming the column is nullable.
You can get the full details on timestamps in MySQL at https://dev.mysql.com/doc/refman/5.0/en/timestamp-initialization.html.
The point that you care about is that if you define a timestamp column as DEFAULT CURRENT_TIMESTAMP clause and don't have an ON UPDATE clause, the column has the current timestamp for its default value but is not automatically updated.
But be warned. The obvious thing to want to do is to have two timestamp columns, one being the creation time and the other being the last update time. Unfortunately it is a documented MySQL limitation that MySQL does not support this. I have no idea why MySQL has such an odd limitation - no other major database has problems with this common use case.
FYI = "Datetime" is date and time fixed. "Timestamp" is variable date and time-- system time.
So, Have two columns. One Create Col, One Update Col.
The following command will create a hello table
1. id integer
2. create_at with current time.
create table hello (id int, created_at datetime DEFAULT CURRENT_TIMESTAMP);
Create Table myTableName
(
userId int primary key
userJoiningDate TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
https://www.mysqltutorial.org/mysql-timestamp.aspx
Here is how you can create a column in which the time stamp is recorded when it is created. If you want to know How to update timeStamp each time that row is changed/updated, Check the above link.
SELECT * FROM test WHERE timestamp >= CURDATE() AND timestamp < CURDATE() + INTERVAL 1 DAY ORDER BY timestamp;