Oracle 12c - how to set 'date' column with year quarter? - sql

I have a need to create a column in Oracle 12c that will be date column with values:
20163
20164
20171
20172
20173
20174
...
How to specify that for a column in a create table statement?
thanks.

You can model the column as containing dates, constrained to fall only on the first moment of each quarter.
CREATE TABLE TEMP
( QUARTER DATE
CONSTRAINT IS_QUARTER CHECK ( (QUARTER = TRUNC(QUARTER, 'Q') ) )
)
To put values into the table, you need to set the date to the start of the quarter:
INSERT INTO temp VALUES ( TO_DATE('2017-04', 'yyyy-mm') );
To read values from the table, you can format as you like:
SELECT TO_CHAR( quarter, 'YYYYQ') FROM temp;
Because the underlying column is a date, you can do things like compare it with other dates, etc.
SELECT TO_CHAR( ADD_MONTHS( quarter, 3 ), 'YYYYQ') FROM temp;

Joe!
Such fields as you described are frequently using for monthly periods.
Typicaly they are encoded in integer datatypes.
If you were not on Oracle, Int32 would be perfect.
In Oracle the decision is not so clear.
I would prefer NUMBER(6,0) and simple constraint that field most be less than any possible date in future.
CHAR(6) - is fine too but constraints will be more complex.

Related

SQL Server - Looking for a way to shorten code

I'm basically very new to SQL Server, so please bare with me. Here is my problem:
I have a table with (let's say) 10 columns and 80k rows. I have 1 column called Date in the format of YYYY-MM-DD type varchar(50) (can't convert it to date or datetime type I tried, the initial source of data is not good).
**Example :
Table [dbo].[TestDates]
Code
SellDate
XS4158
2019-11-26
DE7845
2020-02-06
What I need to do is to turn the YYYY-MM-DD format to DD/MM/YYYY format. After a lot of tries (I tried the functions (DATE_FORMAT, CONVERT, TO_DATE etc) and this is solution :
1- I added a primary key for join purpose later (ID)
2- I split my date column in 3 columns in a whole new table
3- I merged the 3 columns in the order I need with the delimiter of my choice (/) in the same new table
4- I copied the good column to my initial table using the primary key ID I created before
alter table [dbo].[TestDates]
add ID int not null IDENTITY primary key;
SELECT ID,
FORMAT(DATEPART(month, [SellDate]),'00') AS Month,
FORMAT(DATEPART(day, [SellDate]),'00') AS Day,
FORMAT(DATEPART(year, [SellDate]),'0000') AS Year
INTO [dbo].[TestDates_SPLIT]
FROM [dbo].[TestDates]
GO
ALTER TABLE [dbo].[TestDates_SPLIT]
ADD SellDate_OK varchar(50)
UPDATE [dbo].[TestDates_SPLIT]
SET SellDate_OK = [Day] + '/' + [Month] + '/' + [Year]
ALTER TABLE [dbo].[TestDates_SPLIT]
DROP COLUMN Month, Day, Year
ALTER TABLE [dbo].[TestDates]
ADD SellDate_GOOD varchar(50)
UPDATE [dbo].[TestDates]
SET [TestDates].SellDate_GOOD = [TestDates_SPLIT].SellDate_OK
FROM [dbo].[TestDates]
INNER JOIN [dbo].[TestDates_SPLIT]
ON [TestDates].ID = [TestDates_SPLIT].ID
This code works but i find too heavy and long, considering I have 6 more dates columns to work on. Is there a way to make it shorter or more efficient? Maybe with SET SellDate = SELECT (some query of sorts that doesn't require to create and delete table)
Thank you for your help
I tried the usual SQL functions but since my column is a varchar type, the converting was impossible
You should not be storing dates as text. But, that being said, we can try doing a rountrip conversion from text YYYY-MM-DD to date to text DD/MM/YYYY:
WITH cte AS (
SELECT '2022-11-08' AS dt
)
SELECT dt, -- 2022-11-08
CONVERT(varchar(10), CONVERT(datetime, dt, 121), 103) -- 08/11/2022
FROM cte;
Demo

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.

Splitting daterange into columns

I have defined a table in PostgreSQL with a column of type daterange, and now need to split it into two columns of type date (start and end date). How can I do that, I was unable to find anything.
For example, I have [2012-01-01,2015-10-10) in column period and need: start_date 2012-01-01 and end_date 2015-10-10.
lower and upper
https://www.postgresql.org/docs/9.6/static/functions-range.html
create table t (period daterange);
insert into t (period) values ('[2012-01-01,2015-10-10)');
select (period).lower
,(period).upper
from t
;

How to extract the year of a DATE and insert it in a new column?

I have a column in my table which contains DATEs and I need to keep only the YEAR (=> in a NUMBER or INT type)
My idea was to add a column and try something like this :
UPDATE myTable SET newColumn=(
select to_number(to_char(oldColumn.year, 'yyyy')) from myTable)
But I get this error :
single-row subquery returns more than one row
Could you help?!?
You need to change your query to:
UPDATE myTable
SET newColumn=to_number(to_char(oldColumn.year, 'yyyy'))
This SELECT statement will return all of the rows in the table, from column oldColumn.year, which generates your "single-row subquery returns more than one row" error.
(select to_number(to_char(oldColumn.year, 'yyyy')) from myTable)
You can use the EXTRACT function:
UPDATE myTable
SET newColumn = EXTRACT( YEAR FROM oldColumn.year )
It is a bad idea to do that. Storing only a portion of date would hardly be of any use. Also, you won't be able to do any date arithmatic on the column.
To fetch the year value Oracle provides the datetime format for display. For any date calculation based on year you just need to do:
EXTRACT(YEAR FROM date_column)
If you still want to change the design and store only the date part, then instead of creating a static column, use a VIRTUAL COLUMN.
For example,
dt_yyyy NUMBER GENERATED ALWAYS AS (EXTRACT(YEAR FROM date_column)) VIRTUAL
Or,
dt_yyyy NUMBER GENERATED ALWAYS AS (to_number(to_char(date_column, 'YYYY'))) VIRTUAL
NOTE : Virtual columns were introduced from 11g.
insert into table_name (v_date)
values ((select extract (year from current_date())));
This looks like a perfect situation in which to use a virtual column if you are on at least 11g. Consider this create table statement for my test table, where v_year is the virtual column:
CREATE TABLE X_TEST
(
COL2 LONG,
COL3 VARCHAR2(50 BYTE),
COL4 VARCHAR2(50 BYTE),
COL5 VARCHAR2(50 BYTE),
COL6_DATE DATE,
V_YEAR NUMBER Generated Always as (EXTRACT(YEAR FROM "COL6_DATE"))
);
Now, just insert the date and Bam! the year is in there automagically! No special updating needed.
SQL> insert into X_TEST (col6_date) values (sysdate);
1 row created.
SQL> select col6_date, v_year from X_TEST;
COL6_DATE V_YEAR
--------- ----------
22-MAY-15 2015
SQL>
Some more info and examples: http://oracle-base.com/articles/11g/virtual-columns-11gr1.php

Oracle — separate time and date

I have a question with Oracle (I've installed Oracle 11g Express Edition).
I want to insert values for 'date' and 'time', but I cannot separate them.
create table Match
(
numMatch number(2) constraint PKMatch primary key,
dateM date,
heureM date,
numE_Eq number(2),
numE_Eq2 number(2),
nomTerrain varchar2(30)
);
--"tools"=>"preferences"=>"format de date:DD/MM/YYYY HH24:MI:SS"
insert into Match values (1,to_date
('10/12/2010','DD/MM/YYYY'),to_date('15:00:00','HH24:MI:SS'),1,3,'Stade Argentina'
);
result:
dateM: 10/12/2010 00:00:00
heureM: 01/11/2012 15:00:00
PS: I've tried to_char instead of to_date, but it didn't work at all.
Yes, I'm aware of that 'DATE datatype contains both date and time', but it's the prof who insists showing date and time separately in the table,
and I've seen your solutions before, but for me, it's a query, not to 'insert values' in the table.
So I'd like to know how I can have a table directly presenting date and time.
Oracle doesn't have a TIME datatype. You can store a DATE with a time component, and just query based on time, and display based on time.
select to_char(my_date_field, 'HH24:MI:SS')
from my_table
where to_date(my_date_field, 'HH24:MI') = '18:51';
Alternatively, you can store seconds from midnight as an integer, and calculate the time of day from that. It will also make querying for range times easier I think.
Also, within a session, execute the following to have all dates formatted the way you wish:
alter session set nls_date_format = 'YYYY-MM-DD HH24:MI:SS'
Another way to represent a TIME equivalent type in Oracle is with the INTERVAL type, such as:
SQL> CREATE TABLE foo (
bar INTERVAL DAY(0) TO SECOND(3)
);
This would allow the storage of a time period with 0 precision of the DAY component, and 3 decimal points for the SECOND component. An INSERT example is:
SQL> INSERT INTO foo VALUES ('0 01:01:01.333');
What's great about this approach is that it automatically presents the results of a SELECT in an intuitive format without the need for conversion:
SQL> SELECT * FROM foo;
BAR
---------------------------------------------------------------------------
+0 01:01:01.333
but it's the prof who insists showing date and time separately in the table
While this sounds like a pretty stupid requirement, one thing you could do is to create two computed columns that show the date and time as varchar columns:
create table match
(
nummatch number(2) constraint pkmatch primary key,
the_date date,
datem generated always as to_char(the_date, 'yyyy-mm-dd'),
heurem generated always as to_char(the_date, 'hh24:mi')
nume_eq number(2),
nume_eq2 number(2),
nomterrain varchar2(30)
);
insert into Match (nummatch, the_date, nume_eq, nume_eq2, nomterrain)
values
(1,to_date('10/12/2010 15:00:00','DD/MM/YYYY hh24:mi:ss'),1,3,'Stade Argentina');
Then a
select *
from match;
will return:
NUMMATCH | THE_DATE | DATEM | HEUREM | NUME_EQ | NUME_EQ2 | NOMTERRAIN
---------+---------------------+------------+--------+---------+----------+----------------
1 | 2010-12-10 15:00:00 | 2010-12-10 | 15:00 | 1 | 3 | Stade Argentina
Alternatively you could just create a view on the table that separates the date and time using to_char()
Oracle DATE type includes both DATE and TIME information (because it pre-dates the SQL-92 standard when the standard DATE, TIME and TIMESTAMP types were added). So, you can't separate them in the table; there's no reason to do so, either. You can, if you so desire, create a view which presents the DATE field as separate date-only and time-only display fields.