Inserting Day of week and Time in Oracle date column - sql

Considering the following SQL instruction what should I change in order to insert only the day of the week and the time like Hour and Minutes:
INSERT INTO SALA_MATERIA(SALA_ID, MATERIA_ID,HORARIO) VALUES (1,'PT', TO_DATE('FRIDAY 15:00','DAY HH24:MI'));

A DATE always has year, month, day, hours, minutes and seconds. Trying to create a date from just a day of the week and a time does not make sense.
If you want to store just that information then use a VARCHAR2 column and not a DATE.
If you want to store it as a DATE then you will need to specify which Friday and specify a year, month and day as well.

I think you're actually better off storing an entire date - year, month, day, and time - then you can simply use TO_CHAR() or EXTRACT() to get the parts of the date you need. To store a Friday in a DATE column you could do the following:
INSERT INTO sala_materia
( sala_id, materia_id, horario )
VALUES
( 1, 'PT', NEXT_DAY(SYSDATE, 'FRIDAY') );
This will get the current time of day as well; to use an arbitrary time you could truncate the results of the NEXT_DAY function and then use intervals or standard Oracle date arithmetic:
INSERT INTO sala_materia
( sala_id, materia_id, horario )
VALUES
( 1, 'PT', TRUNC( NEXT_DAY(SYSDATE, 'FRIDAY') ) + INTERVAL '15' HOUR );
or
INSERT INTO sala_materia
( sala_id, materia_id, horario )
VALUES
( 1, 'PT', TRUNC( NEXT_DAY(SYSDATE, 'FRIDAY') ) + 15/24 );
Intervals might work a bit better if you need to be more granular (i.e., minutes and seconds).
The reason I recommend this course of action instead of merely storing the day of the week and the time in a VARCHAR2 column is that you can then use TO_CHAR() or EXTRACT() when displaying the date, using date parts in comparisons, etc. - you could even create function-based indexes or virtual columns based on the results of these functions applied to the horario column:
CREATE INDEX mydayofweek ON sala_materia ( TO_CHAR(horario, 'DAY') );

Related

My TO_DATE seeming not to function properly

So I am trying to get the section id and the amount of students in that section who enrolled on 02/10/2007. The query returns no results when it should return 6 rows.
The date format its in already is DD-MON-YY.
This is what I have so far:
I took the TO_DATE from another query I did and it worked properly on. The query works without it so im sure its somthing to do with the TO_DATE
SELECT section_id, COUNT(student_id) "ENROLLED"
FROM enrollment
WHERE enroll_date = TO_DATE('2/10/2007', 'MM/DD/YYYY')
GROUP BY section_id
ORDER BY ENROLLED;
Most probably the issue is that there is a fractional date component that you are not taking into account. You can ignore that fractional date component by truncating the column in your query:
SELECT section_id, COUNT(student_id) "ENROLLED"
FROM enrollment
WHERE TRUNC(enroll_date) = TO_DATE('2/10/2007', 'MM/DD/YYYY')
GROUP BY section_id
ORDER BY ENROLLED;
I am assuming that the column enroll_date is of the data type DATE.
Some explanation: Oracle stores dates as described here, it does NOT store a date as you state "The date format its in already is DD-MON-YY.". That is only the format you see the date in, which is determined by the parameter NLS_DATE_FORMAT for your session.
Lets do a quick test with a test table. Create table and check the NLS_DATE_FORMAT form my session.
create table DATE_TST
( id NUMBER GENERATED BY DEFAULT ON NULL AS IDENTITY,
test_date DATE
);
INSERT INTO date_tst (test_date) VALUES (SYSDATE);
SELECT value
FROM nls_session_parameters
WHERE parameter = 'NLS_DATE_FORMAT';
DD-MON-YYYY
This is how I will see my dates.
SELECT * FROM date_tst;
04-OCT-2020
So I have todays date. Cool. Now lets see if I can query using that date:
SELECT * FROM date_tst WHERE test_date = TO_DATE('04-OCT-2020','DD-MON-YYYY');
no rows.
No rows are shown because the date format I get my date in does not have a time component. DATE has Year, month, day, hour, minute and seconds. The format only has year, month and day. Lets query the data to check if there is a time component.
SELECT TO_CHAR(test_date,'DD-MON-YYYY HH24:MI:SS') FROM date_tst;
4-OCT-2020 21:12:39
Ah there it is... SYSDATE is the current time up to the second. Now lets try that query again with a more precise date format:
SELECT * FROM date_tst WHERE test_date = TO_DATE('04-OCT-2020 21:12:39','DD-MON-YYYY HH24:MI:SS');
04-OCT-2020
And there is our row. The TRUNC command will cut off the time component:
SELECT TO_CHAR(TRUNC(test_date),'DD-MON-YYYY HH24:MI:SS') FROM date_tst;
04-OCT-2020 00:00:00
So you can simplify your query:
SELECT * FROM date_tst WHERE TRUNC(test_date) = TO_DATE('04-OCT-2020','DD-MON-YYYY');
04-OCT-2020
TO_DATE('2/10/2007', 'MM/DD/YYYY') gives you a date at midnight; however,this will only match values at that instant. What you need to do is either:
TRUNCate the dates in your column back to midnight so that your value matches (however, this will prevent you using an index on the column and you would need to use a function-based index); or
A better solution is to use a date range starting at midnight of the day you want to match and going up-to, but not including, midnight of the next day.
You can do this using TO_DATE or using a date literal:
SELECT section_id,
COUNT(student_id) "ENROLLED"
FROM enrollment
WHERE enroll_date >= DATE '2007-02-10'
AND enroll_date < DATE '2007-02-11'
GROUP BY section_id
ORDER BY ENROLLED;
As an aside:
The date format its in already is DD-MON-YY.
Assuming that the enroll_date column has a DATE data type then this has no format; it is a binary data type consisting of 7 bytes (for century, year-of-century, month, day, hour, minute and second).
What you are seeing is the default date format the user interface applies when it displays the binary date value to the user and you can change it using:
ALTER SESSION SET NLS_DATE_FORMAT = 'YYYY-MM-DD"T"HH24:MI:SS';
(or to whatever format you want.)
This does not change the binary data stored in the column.

SQL- Math with Dates

I am working out of Oracle SQL. I have some dates that may have been poorly formatted when loading.
I'm doing a basic Max(date)-Min(Date) to get the difference in days. My results are:
+000000156 00:00:00.000000
+000000149 00:00:00.000000
+00 00:00:00.000000
I want to do some basic math with these date differences (average, etc) but I get an error message.
How do I convert these strings into numbers?
My guess is that the columns are timestamps, not dates, as the results are intervals not numbers. As you've found, Oracle have not got around to overloading the standard aggregate functions for intervals (vote for this feature on the Oracle Database Ideas forum) and currently you still have to either write your own or cast the timestamps to dates.
with demo (start_date, end_date) as
( select timestamp '2019-12-31 00:00:00', timestamp '2020-06-04 00:00:00' from dual union all
select timestamp '2020-01-31 00:00:00', timestamp '2020-06-28 00:00:00' from dual
)
select end_date - start_date as elapsed_interval
, cast(end_date as date) - cast(start_date as date) as elapsed_days
from demo;
ELAPSED_INTERVAL ELAPSED_DAYS
----------------------------- ------------
+000000156 00:00:00.000000000 156
+000000149 00:00:00.000000000 149
Basic math with dates:
date + number = date + number of days (also fractions)
SELECT SYSDATE + 1 FROM DUAL; -- tomorrow
date - number = date - number of days
SELECT SYSDATE - 1/24 FROM DUAL; -- one hour ago
date - date = numbers of days between dates (also fraction of days)
date + date = impossible
months_between(date1, date2) = returns months between two dates
add_months(date, number) = adds number (months) to date
if you have a string or number and it can be the n-th day of the year (for instance 156),
you can transform in date with TO_DATE('156', 'DDD')
if you have a string with a particular format, you can transform it in date with
TO_DATE(string, format of the date you imagine)
https://www.techonthenet.com/oracle/functions/to_date.php
if you need the opposite transform, that is transforming date to char (or number), use TO_CHAR(date, format of the date)

When and why does the TRUNC(DATE, [FORMAT]) used in the real world

I am getting into Oracle database. I came across the TRUNC(DATE, [FMT]) function. I am not really clear on it except it seems to return the beginning value of some sort?
Can somebody educate me on it? When or what would it be used for at work, or why somebody might want to use the function?
Try this query to know when it might be usefull:
ALTER SESSION SET NLS_DATE_FORMAT = 'yyyy-mm-dd hh24:mi:ss';
select sysdate,
trunc( sysdate, 'mi' ) As beginning_of_current_minute,
trunc( sysdate, 'mi' ) As beginning_of_current_hour,
trunc( sysdate, 'dd' ) As beginning_of_current_day,
trunc( sysdate, 'iw' ) As beginning_of_current_week,
trunc( sysdate, 'mm' ) As beginning_of_current_month,
trunc( sysdate, 'q' ) As beginning_of_current_Quarter,
trunc( sysdate, 'y' ) As beginning_of_current_Year
FROM dual;
An example - you want to get all orders starting from the beginning of the current week:
SELECT *
FROM ORDERS
WHERE order_date >= trunc( sysdate, 'iw' )
A real world example would be if you wanted to aggregate results from a table by year. You could use the TRUNC function like this:
SELECT TRUNC(my_date, 'YEAR') the_year, count(*)
FROM some_table
GROUP BY TRUNC(my_date, 'YEAR');
...which would return a set of results with the first column the date truncated to the beginning of the year and the second column a count of all the records with dates within that year.
the_year, count(*)
_________________
01-JAN-12, 543
01-JAN-13, 1268
01-JAN-14, 1134
01-JAN-15, 1765
There are obviously other ways to achieve the same thing, but this is a real world example of how you might use TRUNC.
Another might be if you are comparing dates and you only want to use a certain degree of precision. If you have a timestamp column and you want all the records for today, you could select based on a range where the timestamp is greater than midnight yesterday and less than midnight today, or you could select where the timestamp, truncated to the DATE, is equal to today.
https://docs.oracle.com/cd/E29805_01/server.230/es_eql/src/cdfp_analytics_lang_trunc.html
Another thing it is useful for is to get the time component of the current day. I use an expression like this all the time:
SELECT sysdate - trunc(sysdate) AS TodaysTime FROM DUAL
Because the system date is stored in a decimal format (e.g. sysdate = 42651.2426897456) and the integer value corresponds to midnight, I can use the above statement to get only the decimal portion (e.g. TodaysTime = 0.2426897456, or just before 6 AM).
There may be easier ways to do this, but in my applications this has been the easiest as I frequently need to work with only the day's time component.

Get day of week from time period

I have two date from user input. I want to get from jsp these two dates and create a view which shows each day with their own weekday:
2013-3-1 MONDAY
2013-3-2 TUESDAY
2013-3-3 WEDNESDAY
User inputs date and gives to jsp, say:
where start='2013-3-1' and end='2013-3-3'
How to do this?
This can be simpler:
SELECT to_char(d, 'YYYY-MM-DD') AS day_txt
, to_char(d, 'FMDay') AS weekday
FROM generate_series('2013-03-01'::date
, '2013-03-03'::date
, interval '1 day') d;
More details under this later question:
Remove blank-padding from to_char() output
Generally it is better to operate with actual date types, not with text representations, but I did not understand the details or your requirements.
Try this:
select to_char(dt, 'yyyy-mm-dd') as date, to_char(dt, 'Day') as day
from (select ('2013-03-01'::date + i) dt
from generate_series(0,'2013-03-03'::date - '2013-03-01'::date) as t(i)) as t;
You should parameterize the date part of query.
There are a couple of points worth remember in this query.
date calculation: subtract two date become the number of dates between two dates.
generate_series(0, n) generates 0, 1, ... n

Compare date + time with timestamp

I have a table with two temporal columns. First (name is DATE) is storing the date (not including the time part) and therefor the datatype is DATE. Second column (name is TIME) is for storing the time in seconds and therefor the datatype is NUMBER.
I need to compare this two dates with a timestamp from another table. How can I calculate the date of the two columns (DATE and TIME) and compare to the timestamp of the other table?
I have tried to calculate the hours out of the time column and add it to the date column, but the output seems not correct:
SELECT to_date(date + (time/3600), 'dd-mm-yy hh24:mi:ss') FROM mytable;
The output is just the date, but not the time component.
You can use the INTERVAL DAY TO SECOND type:
SELECT your_date + NUMTODSINTERVAL(your_time_in_seconds, 'SECOND') FROM dual;
Example:
SELECT TRUNC(SYSDATE) + NUMTODSINTERVAL(39687, 'SECOND') FROM dual;
The calculated date with time is: 10-11-2013 11:01:27
This is a better idea than dividing your value by 3600 in my opinion, as you have an interval in seconds, so it feels natural to use an interval to represent your time, which can then be easily added to a column of DATE datatype.
Oracle Interval in Documentation
NUMTODSINTERVAL Function in documentation
date + (time/3600) is already a DATE, so you don't need to do to_date(). It does have the time part you added though, you just aren't displaying it. If you want to output that as a string in the format you've shown, use to_char() instead:
SELECT to_char(date + (time/3600), 'dd-mm-yy hh24:mi:ss') FROM mytable;
... except that if time is actually in seconds, you need to divide by 86400 (24x60x60), not 3600. At the moment you're relying on your client's default date format, probably NLS_DATE_FORMAT, which doesn't include the time portion from what you've said. That doesn't mean the time isn't there, it just isn't displayed.
But that is just for display. Leave it as a date, by just adding the two values, when comparing against you timestamp, e.g.
WHERE date + (time/86400) < systimestamp
Try like this,
SELECT TO_DATE('11/11/2013','dd/mm/yyyy') + 3600/60/60/24 FROM DUAL;
Your query,
SELECT date + time/60/60/24 FROM mytable;
try using to_timestamp instead of to_date