Perfect
Kim Njeru, November 21, 2002 - 5:24 pm UTC
You did it again Tom!
Date calculations are something that most DBA's don't know enough about but should know more about.
I think there is a flaw in the solution
Jon, January 16, 2003 - 1:51 pm UTC
Tom, I think there is a problem with this solution, at least in my mind. When trying to get the difference between 28-feb-1994 and 31-mar-1996, I get 2 years, 1 month and 0 days. I happen to think that the answer should be 2 years, 1 month, and 3 days, but perhaps that is just definitional, as 28-feb-1994 and 31-mar-1996 are both the last day of the month. But that got me to wondering what would we get if we looked for the difference between 28-feb-1994 and 30-mar-1996. The answer is 2 years, 1 month and MINUS 1 days. Can a date difference be both positive and negative? Similarly, 29-MAR yields 2 days and 28-MAR yields 1 day. 27-MAR correctly yields 2 years, 0 months and 27 days.
Here is the script to create the data:
drop table t1;
create table t ( end_date date, start_date date );
insert into t values ( '27-MAR-1996','28-FEB-1994' );
insert into t values ( '28-MAR-1996','28-FEB-1994' );
insert into t values ( '29-MAR-1996','28-FEB-1994' );
insert into t values ( '30-MAR-1996','28-FEB-1994' );
insert into t values ( '31-MAR-1996','28-FEB-1994' );
and here are the results:
SQL> select end_date, start_date ,
2 trunc( months_between( end_date, start_date ) /12 ) Years,
3 mod( trunc( months_between( end_date, start_date ) ), 12 ) months,
4 end_date - add_months(start_date,trunc( months_between(end_date,start_date
5 ) )) days
6 from t
7 /
END_DATE START_DATE YEARS MONTHS DAYS
----------- ----------- --------- --------- ---------
27-mar-1996 28-feb-1994 2 0 27
28-mar-1996 28-feb-1994 2 1 -3
29-mar-1996 28-feb-1994 2 1 -2
30-mar-1996 28-feb-1994 2 1 -1
31-mar-1996 28-feb-1994 2 1 0
Is there a better way to do this?
January 16, 2003 - 7:53 pm UTC
cool -- needs a tad bit of work eh...
boundary values, gotta hate them...
easiest thing is to zero out negative days -- it is as valid as anything else. But if you figure out what you *want* returned, it should be easy to add it.
You can use a simple greatest( 0, end_date - .... ) to zero it out.
Difference Between Two Dates
Khawar Iqbal Pasha, September 02, 2003 - 2:48 am UTC
Accumulating your tips and some of my experience i've written this function. May it would be helpful for others.
create or replace function date_diff ( p_date1 DATE , p_date2 DATE)
return char
is
Years NUMBER;
months NUMBER;
days NUMBER;
day_fraction NUMBER;
hrs NUMBER;
mints NUMBER;
sec NUMBER;
begin
Years :=trunc( months_between( p_date2 , p_date1 ) /12 );
months:=mod( trunc( months_between( p_date2, p_date1 ) ), 12 );
days :=trunc(p_date2 - add_months(p_date1,trunc(months_between(p_date2,p_date1) )));
day_fraction:= (p_date2-p_date1)-trunc(p_date2-p_date1);
hrs :=trunc(day_fraction*24);
mints :=trunc((((day_fraction)*24)-(hrs))*60);
sec :=trunc(mod((p_date2-p_date1)*86400,60));
return(years||' Years '||months||' Months '||days||' Days '||hrs||' Hours '||mints||' Minutes '||sec||' Seconds');
end;
/
September 02, 2003 - 7:29 am UTC
Reverse function
A reader, September 10, 2003 - 7:37 am UTC
What if I have want to convert 31000003170010.6 seconds into yyyy/mm/dd hh:mi:ss
September 10, 2003 - 7:37 pm UTC
hmm, is that just a random number or what ?
Not a random number
A reader, September 11, 2003 - 10:04 am UTC
I was actually asked this question? My initial response, That's just insane. I realize it would approximately equate to 100,000 years.
But, using a more realistic number, say 1,728,100 seconds, is it possible ?
September 11, 2003 - 6:04 pm UTC
sure, assuming the seconds are "since some time".
eg: lets say that is a "C time" or "unix time" -- the number of seconds since 1/1/1970
ops$tkyte@ORA920> variable theTime number
ops$tkyte@ORA920>
ops$tkyte@ORA920> exec :theTime := 1728100
PL/SQL procedure successfully completed.
ops$tkyte@ORA920>
ops$tkyte@ORA920> SELECT to_char(
2 new_time( to_date('01011970','ddmmyyyy') + 1/24/60/60 * :theTime,
3 'GMT', 'EDT' ), 'dd-mon-yyyy hh24:mi:ss' )
4 from dual;
TO_CHAR(NEW_TIME(TO_
--------------------
20-jan-1970 20:01:40
ops$tkyte@ORA920>
does that.
Calculating the difference between fields values to days and hours and minutes
Jamil, December 20, 2003 - 3:39 am UTC
Dear Sir
I have this problem ,my program calculate the journey times and install it in to this fields as the following
Journey_day char(5)
Journey_hour char(5)
Journey_ Minutes char(5)
And this field have a fixed value for expected time consume ,as the following fields
Expected_day char(5)
Expected_hour char(5)
Expected_ Minutes char(5)
So I want to calculate the difference between the above fields , for example
If the above fields have this values as follow :
-------------------
Varying fields values
Journey_day = 0
Journey_hour = 23
Journey_ Minutes = 30
------------------
This fixed fields values
Expected_day = 1
Expected_hour = 10
Expected_ Minutes = 0
So I want to calculated the difference between the above fields And install it in this fields as follow :
Result_day = to_number(Journey_day - Expected_day)
Resul_hour = to_number(Journey_hour - Expected_hour)
Result_ Minutes =to_number(Journey_Minutes- Expected_ Minutes)
The above statements it will not give the Wright result .
The required thing how can I calculate the difference between the above fields so that I will get the correct result in to those fields Result_day , Resul_hour, and Result_ Minutes
Please help
Best regards
Jamil
December 20, 2003 - 10:22 am UTC
ops$tkyte@ORA920> select t.*,
2 trunc( e-j ) "Dy",
3 trunc( mod( (e-j)*24, 24 ) ) "Hr",
4 trunc( mod( (e-j)*24*60, 60 ) ) "Mi"
5 from (
6 select t.*,
7 trunc(sysdate,'y')+jd+jh/24+jm/24/60 j,
8 trunc(sysdate,'y')+ed+eh/24+em/24/60 e
9 from t
10 ) t
11 /
JD JH JM ED EH EM J E Dy Hr Mi
--- --- --- --- --- --- -------- -------- --- --- ---
0 23 30 1 10 0 01 23:30 02 10:00 0 10 30
Use date arithmetic. if you are using 9i, you can use an interval type instead of individual fields.
Calculating the difference between fields values to days and hours and minutes
Jamil, December 21, 2003 - 4:08 am UTC
Dear Sir ,
Thank you very much for your valuable answer , that is what I want
but my data in tow tables and the table structure
as follow :
SQL> descr transaction_file
Name Null? Type
-----------------------------------------
TRANSACTION_CODE NUMBER
B_CODE NUMBER
JD CHAR(5)
JH CHAR(5)
JM CHAR(5)
SQL> descr city
Name Null? Type
-------------------------------------------
CITY_CODE NUMBER
ED CHAR(5)
EH CHAR(5)
EM CHAR(5)
And the selected data from both tables as follow :
Table transaction_file contains:
TRANSACTION_CODE B_CODE JD JH JM
-------------------------------------------------
1 2 0 23 30
2 2 1 17 21
3 1 0 2 0
Table city contains:
CITY_CODE ED EH EM
-------------------------------
1 0 2 0
2 1 10 0
so I want the select statement to select from the above
tow tables
and give the number of days , hours ,and minutes
and the link between the tow tables as follow
B_CODE = CITY_CODE , for each transaction_code
it should calculate the result .
Notice:
I want to install the number of days ,number of hours
and the number of minutes in a variable
So that I want to install the result in another table.
I try to do it like this, but it does not work
line 12 truncated.
1 select trunc( e-j ) "Dy",
2 trunc( mod( (e-j)*24, 24 ) ) "Hr",
3 trunc( mod( (e-j)*24*60, 60 ) ) "Mi"
4 from (
5 select
6 trunc(sysdate,'y')+jd+jh/24+jm/24/60 j,
7 trunc(sysdate,'y')+ed+eh/24+em/24/60 e
8 from city A
9 WHERE A.CITY_CODE = B.B_CODE
10 and B.TRANSACTION_CODE = B.TRANSACTION_CODE
11* ) TRANSACTION_FILE B INTO R_DAYS,R_HOURS,R_MINUTES
12 /
) TRANSACTION_FILE B INTO R_DAYS,R_HOURS,R_MINUTES
*
ERROR at line 11:
ORA-00933: SQL command not properly ended
Tank you very much for your great help.
Best regards
Jamil
December 21, 2003 - 10:32 am UTC
lose the "into r_days,r_hours,r_minutes" first of all.
that only works in programatic environments.
it would be after the select list -- NOT at the end of the list.
actually -- your entire query is sort of "not really SQL at all"
but, your description -- if accurate -- makes this a pretty simple problem. Just work with the data:
ops$tkyte@ORA920> select *
2 from city c, transaction_file tf
3 where c.city_code = tf.b_code
4 /
CITY_CODE ED EH EM TRANSACTION_CODE B_CODE JD JH JM
---------- ---------- ---------- ---------- ---------------- ---------- ---------- ---------- ----------
1 0 2 0 3 1 0 2 0
2 1 10 0 1 2 0 23 30
2 1 10 0 2 2 1 17 21
Now, as that is a multi-row result set there will be no "into" clause (that works with a row only)... so
ops$tkyte@ORA920> select transaction_code,
2 trunc(sysdate,'y')+jd+jh/24+jm/24/60 j,
3 trunc(sysdate,'y')+ed+eh/24+em/24/60 e
4 from city c, transaction_file tf
5 where c.city_code = tf.b_code
6 /
TRANSACTION_CODE J E
---------------- -------------------- --------------------
3 01-jan-2003 02:00:00 01-jan-2003 02:00:00
1 01-jan-2003 23:30:00 02-jan-2003 10:00:00
2 02-jan-2003 17:21:00 02-jan-2003 10:00:00
<b>but that points out an interesting anomoly in your data -- your end times are sometimes before the "starts", so, you'll get negative results:</b>
ops$tkyte@ORA920> select transaction_code,
2 trunc( e-j ) "Dy",
3 trunc( mod( (e-j)*24, 24 ) ) "Hr",
4 trunc( mod( (e-j)*24*60, 60 ) ) "Mi"
5 from (
6 select transaction_code,
7 trunc(sysdate,'y')+jd+jh/24+jm/24/60 j,
8 trunc(sysdate,'y')+ed+eh/24+em/24/60 e
9 from city c, transaction_file tf
10 where c.city_code = tf.b_code
11 )
12 /
TRANSACTION_CODE Dy Hr Mi
---------------- ---------- ---------- ----------
3 0 0 0
1 0 10 30
2 0 -7 -21
Calculating the difference between fields values to days and hours and minutes
Jamil, December 22, 2003 - 6:12 am UTC
Dear Sir
As usual great response from Tom,Thank you very much for your answer and it works fine, that is actually what I
want.
Can you clarify these statements is not possible to do the calculation without this statements
trunc(sysdate,'y')
Best regards
Jamil
December 22, 2003 - 9:11 am UTC
you certainly can do it without trunc(sysdate,'y')
i used that cause printing out the dates looks "sensible" (they'll be offset from the first day of the year. so your duration of 5 days 10 hours 20 minutes would be 10:20 am on jan 5th -- that was sensible to me as I was developing the query, something I could understand. You could use sysdate or any date really -- but using the first of the year made debugging this easy)
Date difference in seconds
Tony, January 30, 2004 - 10:52 am UTC
Hi tom,
Two date columns have date and time stored.
How to find the date difference in seconds?
I want day, month, minute and years difference everyting in seconds.
January 30, 2004 - 7:26 pm UTC
select (dt1-dt2) * 24 * 60 * 60 from t;
dt1-dt2 gives diff in days (eg: 1.2323 days)
multiply by 24 = hours, another 60 = minutes, another 60 = seconds
Negative Seconds
A reader, March 09, 2004 - 4:01 pm UTC
March 09, 2004 - 10:31 pm UTC
do you have an example? (you do need to put the "bigger" date in first -- you can use greatest(a,b) where I used sysdate and least(a,b) where I used created)
Here is the example
A reader, March 11, 2004 - 9:47 am UTC
Hi,
Did I do something wrong?
select to_char( created, 'dd-mon-yyyy hh24:mi:ss' ),
trunc( sysdate-created ) "Dy",
trunc( mod( (sysdate-created)*24, 24 ) ) "Hr",
trunc( mod( (sysdate-created)*24*60, 60 ) ) "Mi",
trunc( mod( to_char(sysdate,'SSSSS')-to_char(created,'SSSSS'), 60 ) ) "Sec",
to_char( sysdate, 'dd-mon-yyyy hh24:mi:ss' ),
sysdate-created "Tdy",
(sysdate-created)*24 "Thr",
(sysdate-created)*24*60 "Tmi",
(sysdate-created)*24*60*60 "Tsec"
from all_users
where rownum < 50
And the result:
TO_CHAR(CREATED,'DD- Dy Hr Mi Sec TO_CHAR(SYSDATE,'DD-
-------------------- ---------- ---------- ---------- ---------- --------------------
10-jun-2002 13:48:17 639 19 0 -55 11-mar-2004 08:48:22
10-jun-2002 13:48:18 639 19 0 -56 11-mar-2004 08:48:22
10-jun-2002 13:48:38 639 18 59 -16 11-mar-2004 08:48:22
01-may-2003 09:28:45 314 23 19 -23 11-mar-2004 08:48:22
11-jun-2002 08:59:24 638 23 48 -2 11-mar-2004 08:48:22
03-apr-2003 13:59:51 342 18 48 -29 11-mar-2004 08:48:22
10-jun-2002 15:10:09 639 17 38 -47 11-mar-2004 08:48:22
10-jun-2002 15:10:11 639 17 38 -49 11-mar-2004 08:48:22
10-jun-2002 15:10:11 639 17 38 -49 11-mar-2004 08:48:22
10-jun-2002 15:17:16 639 17 31 -54 11-mar-2004 08:48:22
10-jun-2002 16:14:21 639 16 34 -59 11-mar-2004 08:48:22
10-jun-2002 16:14:21 639 16 34 -59 11-mar-2004 08:48:22
10-jun-2002 16:14:21 639 16 34 -59 11-mar-2004 08:48:22
10-jun-2002 16:14:21 639 16 34 -59 11-mar-2004 08:48:22
10-jun-2002 16:14:21 639 16 34 -59 11-mar-2004 08:48:22
10-jun-2002 16:14:21 639 16 34 -59 11-mar-2004 08:48:22
10-jun-2002 16:14:24 639 16 33 -2 11-mar-2004 08:48:22
10-jun-2002 16:14:24 639 16 33 -2 11-mar-2004 08:48:22
10-jun-2002 16:14:25 639 16 33 -3 11-mar-2004 08:48:22
10-jun-2002 16:14:25 639 16 33 -3 11-mar-2004 08:48:22
11-jun-2002 08:59:24 638 23 48 -2 11-mar-2004 08:48:22
11-jun-2002 08:59:24 638 23 48 -2 11-mar-2004 08:48:22
11-jun-2002 08:59:24 638 23 48 -2 11-mar-2004 08:48:22
11-jun-2002 08:59:24 638 23 48 -2 11-mar-2004 08:48:22
11-jun-2002 08:59:24 638 23 48 -2 11-mar-2004 08:48:22
11-jun-2002 08:59:24 638 23 48 -2 11-mar-2004 08:48:22
11-jun-2002 08:59:24 638 23 48 -2 11-mar-2004 08:48:22
11-jun-2002 08:59:24 638 23 48 -2 11-mar-2004 08:48:22
11-jun-2002 08:59:24 638 23 48 -2 11-mar-2004 08:48:22
11-jun-2002 08:59:24 638 23 48 -2 11-mar-2004 08:48:22
11-jun-2002 08:59:24 638 23 48 -2 11-mar-2004 08:48:22
11-jun-2002 08:59:24 638 23 48 -2 11-mar-2004 08:48:22
11-jun-2002 08:59:24 638 23 48 -2 11-mar-2004 08:48:22
11-jun-2002 08:59:24 638 23 48 -2 11-mar-2004 08:48:22
11-jun-2002 08:59:24 638 23 48 -2 11-mar-2004 08:48:22
11-jun-2002 08:59:24 638 23 48 -2 11-mar-2004 08:48:22
11-jun-2002 08:59:24 638 23 48 -2 11-mar-2004 08:48:22
11-jun-2002 08:59:24 638 23 48 -2 11-mar-2004 08:48:22
11-jun-2002 08:59:24 638 23 48 -2 11-mar-2004 08:48:22
11-jun-2002 08:59:24 638 23 48 -2 11-mar-2004 08:48:22
11-jun-2002 08:59:24 638 23 48 -2 11-mar-2004 08:48:22
11-jun-2002 08:59:25 638 23 48 -3 11-mar-2004 08:48:22
11-jun-2002 08:59:25 638 23 48 -3 11-mar-2004 08:48:22
11-jun-2002 08:59:25 638 23 48 -3 11-mar-2004 08:48:22
11-jun-2002 08:59:25 638 23 48 -3 11-mar-2004 08:48:22
11-jun-2002 08:59:25 638 23 48 -3 11-mar-2004 08:48:22
11-jun-2002 08:59:25 638 23 48 -3 11-mar-2004 08:48:22
11-jun-2002 08:59:25 638 23 48 -3 11-mar-2004 08:48:22
11-jun-2002 08:59:25 638 23 48 -3 11-mar-2004 08:48:22
March 11, 2004 - 1:48 pm UTC
looks like its the math
trunc( mod( to_char(sysdate,'SSSSS')-to_char(created,'SSSSS'), 60 ) )
sssss is seconds past midnight. that'll be negative if sysdate is "early" in the day
ops$tkyte@ORA9IR2> select to_char( d1, 'dd-mon-yyyy hh24:mi:ss' ),
2 to_char( d2, 'dd-mon-yyyy hh24:mi:ss' ),
3 trunc( d1-d2 ) "Dy",
4 trunc( mod( (d1-d2)*24, 24 ) ) "Hr",
5 trunc( mod( (d1-d2)*24*60, 60 ) ) "Mi",
6 trunc( mod( to_char(d1,'SSSSS')-to_char(d2,'SSSSS'), 60 ) ) "Sec"
7 from (select sysdate d1, sysdate-1+5/24/60/60 d2 from dual )
8 /
TO_CHAR(D1,'DD-MON-Y TO_CHAR(D2,'DD-MON-Y Dy Hr Mi Sec
-------------------- -------------------- ----- ------- ---------- ----------
11-mar-2004 12:43:38 10-mar-2004 12:43:43 0 23 59 -5
quick fix, haven't tested all boundary conditions:
ops$tkyte@ORA9IR2> select d1s, d2s, to_char(d2+"Hr"/24+"Mi"/24/60+"Sec"/24/60/60,'dd-mon-yyyy hh24:mi:ss') new,
2 "Dy", "Hr", "Mi", "Sec"
3 from (
4 select to_char( d1, 'dd-mon-yyyy hh24:mi:ss' ) d1s,
5 to_char( d2, 'dd-mon-yyyy hh24:mi:ss' ) d2s,
6 d2,
7 trunc( d1-d2 ) "Dy",
8 trunc( mod( (d1-d2)*24, 24 ) ) "Hr",
9 trunc( mod( (d1-d2)*24*60, 60 ) ) "Mi",
10 trunc( mod( (d1-d2)*24*60*60, 60 ) ) "Sec"
11 from (select sysdate d1, sysdate-1+5/24/60/60 d2 from dual )
12 )
13 /
D1S D2S NEW Dy Hr Mi Sec
-------------------- -------------------- -------------------- ---------- ---------- ---------- ----------
11-mar-2004 12:48:27 10-mar-2004 12:48:32 11-mar-2004 12:48:27 0 23 59 55
another would be to use decode( sign("Sec"), -1, 60+"Sec", "Sec" )
(can't believe no one has noticed that before!)
Once again to_char(date) rules!
andrew, March 12, 2004 - 3:06 pm UTC
SQL> column the_diff format a30
SQL> select -- method 1
2 TO_CHAR (TRUNC (SYSDATE-CREATED), '000') || ' days ' ||
3 TO_CHAR (TRUNC (SYSDATE) + MOD (SYSDATE-CREATED, 1),
4 'HH24 "hrs" MI "min" SS "sec"') the_diff,
5 -- method 2
6 trunc(sysdate-created ) "Dy",
7 trunc(mod((sysdate-created)*24, 24 )) "Hr",
8 trunc(mod((sysdate-created)*24*60, 60 )) "Mi",
9 trunc(mod(to_char(sysdate,'SSSSS')-to_char(created,'SSSSS'), 60)) "Sec"
10 from all_users
11 where rownum < 5;
THE_DIFF Dy Hr Mi Sec
------------------------------ ---------- ---------- ---------- ----------
967 days 21 hrs 37 min 16 sec 967 21 37 -44
967 days 21 hrs 37 min 16 sec 967 21 37 -44
967 days 21 hrs 36 min 57 sec 967 21 36 -3
029 days 18 hrs 17 min 15 sec 29 18 17 -45
expected DATE got DATE
Justin, April 25, 2004 - 8:04 am UTC
Tom,
This is strange.
This works fine:
wic_owner5@DEV> select sysdate - add_months(sysdate,12) from dual;
SYSDATE-ADD_MONTHS(SYSDATE,12)
==============================
-365
This doesn't with a funny error message.
What am I doing wrong?
I just want to update the value of the date field to 1 year in the past.
wic_owner5@DEV> update t069_person set BIRTH_DT = sysdate - add_months(sysdate,12) where t069_id =8748;
update t069_person set BIRTH_DT = sysdate - add_months(sysdate,12) where t069_id =8748
*
ERROR at line 1:
ORA-00932: inconsistent datatypes: expected DATE got DATE
All similar attempts, other than sysdate - ### seem to not work.
wic_owner5@DEV> update loser set val = sysdate - (sysdate + 20);
update loser set val = sysdate - (sysdate + 20)
*
ERROR at line 1:
ORA-00932: inconsistent datatypes: expected DATE got DATE
Thank you very much for your time.
April 26, 2004 - 5:46 am UTC
dateA - dateB = days between the two (a number).
if you want a field to be a year in the past, you
update t
set x = add_months(x,-12)
where ..........
or
set x = add_months(sysdate,-12)
thanks very much
Justin, April 26, 2004 - 11:11 am UTC
The error message threw me. Still a dumbo on my part.
Thanks!
months between
Pukula, August 02, 2004 - 8:24 am UTC
Hi
I want to find orders in one month in a year say July
I do
order_date between to_date('200407', 'YYYYMM') and to_date('200408', 'YYYYMM')
It´s wrong because I am including 20040801 00:00:00, I then do
order_date >= to_date('200407', 'YYYYMM')
and order_date < to_date('200408', 'YYYYMM')
I would like to know if there is a way to do this with between and not using HH:MI:SS?
Cheers
August 02, 2004 - 8:51 am UTC
order_date between to_date('200407', 'YYYYMM')
and to_date('200408', 'YYYYMM')-1/24/60/60
if you use timestamps instead of dates, same concept -- just be subtracting a smaller number.
How can I get a date back from Varchar2
Venkat, August 10, 2004 - 5:37 pm UTC
Hi Tom,
There is a table with dates stored as varchar2. They are derived using the conversion as follows in a PLSQL procedure:
to_char(<some_date>, 'YYYY-MM-DD HH24:MI:SSSSS').
Now, I want to convert this VARCHAR2 value as a date.
drop table t;
create table t ( ts varchar2(30) )
/
insert into t values ( '2004-07-30 07:07:25972' )
/
insert into t values ('2004-07-30 17:07:62891' )
/
select ts from t;
TS
----------------------
2004-07-30 07:07:25972
2004-07-30 17:07:62891
How do I convert the SSSSS component above into a date format.
When I try this, I get an error:
select ts, to_date(ts, 'yyyy-mm-dd hh24:mi:sssss') dt
from t
/
select ts, to_date(ts, 'yyyy-mm-dd hh24:mi:sssss') dt
*
ERROR at line 1:
ORA-01837: minutes of hour conflicts with seconds in day
encompass@tkd16> l
1* select * from nls_session_parameters
encompass@tkd16> /
PARAMETER VALUE
------------------------------ ----------------------------------------
NLS_LANGUAGE AMERICAN
NLS_TERRITORY AMERICA
NLS_CURRENCY $
NLS_ISO_CURRENCY AMERICA
NLS_NUMERIC_CHARACTERS .,
NLS_CALENDAR GREGORIAN
NLS_DATE_FORMAT DD-MON-YY
NLS_DATE_LANGUAGE AMERICAN
NLS_SORT BINARY
NLS_TIME_FORMAT HH.MI.SSXFF AM
NLS_TIMESTAMP_FORMAT DD-MON-RR HH.MI.SSXFF AM
NLS_TIME_TZ_FORMAT HH.MI.SSXFF AM TZR
NLS_TIMESTAMP_TZ_FORMAT DD-MON-RR HH.MI.SSXFF AM TZR
NLS_DUAL_CURRENCY $
NLS_COMP BINARY
NLS_LENGTH_SEMANTICS BYTE
NLS_NCHAR_CONV_EXCP FALSE
Regards,
Venkat
August 10, 2004 - 7:42 pm UTC
hmmm, did the people understand what sssss is? that is "seconds past midnight"
they probably thought "hey, we are able to magically get hundredths of seconds" (wrong, not happening)
you'll have to trunc(that sssss part/60)
or substring out the hh:mi part.
ops$tkyte@ORA9IR2> select to_date( substr( ts, 1, 10 ) || substr( ts, 18 ), 'yyyy-mm-ddsssss' )
2 from t;
TO_DATE(SUBSTR(TS,1,
--------------------
30-jul-2004 07:12:52
30-jul-2004 17:28:11
sigh, sigh, sigh, the old "store a date in a string" - what a bad bad horrible idea. ranks up there in the top 5 things I *hate* (right next to number in a string)
Hmmm..Minutes do not match
Venkat, August 11, 2004 - 1:39 pm UTC
Tom,
You said:
<
hmmm, did the people understand what sssss is? that is "seconds past midnight"
they probably thought "hey, we are able to magically get hundredths of seconds"
(wrong, not happening)
> - I guess that might have been the impression
Now, how do we explain the fact that the minutes stored in HH:MI is different from the minutes derived by converting the sssss component.
Here is the example:
select ts ,
to_char(
to_date( substr( ts, 1, 10 ) || substr( ts, 18 ),
'yyyy-mm-ddsssss'),
'yyyy-mm-dd hh24:mi:ss') ch_dt
from t
TS CH_DT
------------------------------ -------------------
2004-07-30 07:07:25972 2004-07-30 07:12:52
2004-07-30 17:07:62891 2004-07-30 17:28:11
See the minute component in the second column is different. Instead of 07, it is 12 and 28 in the first and second record respectively.
Hmmm....
I tried to do this to replicate the original date to varchar2 conversions.
insert into t
select to_char(sysdate, 'YYYY-MM-DD HH24:MI:SSSSS')
from dual
/
1 row created.
And then I ran the query again.
select ts ,
to_char(
to_date( substr( ts, 1, 10 ) || substr( ts, 18 ),
'yyyy-mm-ddsssss'),
'yyyy-mm-dd hh24:mi:ss') ch_dt
from t
/
TS CH_DT
------------------------------ -------------------
2004-07-30 07:07:25972 2004-07-30 07:12:52
2004-07-30 17:07:62891 2004-07-30 17:28:11
2004-08-11 12:24:44689 2004-08-11 12:24:49
The new record looks ok after the conversion but why are the original ones off by a few minutes?
Venkat
August 11, 2004 - 2:37 pm UTC
sorry -- but your numbers do not "jive" then. you have:
select ts ,
to_char(
to_date( substr( ts, 1, 10 ) || substr( ts, 18 ),
'yyyy-mm-ddsssss'),
'yyyy-mm-dd hh24:mi:ss') ch_dt
from t
TS CH_DT
------------------------------ -------------------
2004-07-30 07:07:25972 2004-07-30 07:12:52
2004-07-30 17:07:62891 2004-07-30 17:28:11
now, if the format was really truly:
YYYY-MM-DD HH24:MI:SSSSS
then the sssss number does not compute -- do the math:
ops$tkyte@ORA9I> select 7*60*60+7*60, 7*60*60+8*60 from dual
2 /
7*60*60+7*60 7*60*60+8*60
------------ ------------
25620 25680
ops$tkyte@ORA9I> select 7*60*60+12*60+52 from dual
2 /
7*60*60+12*60+52
----------------
25972
25972 cannot be between 25620 and 25680
that 25972 cannot be at 07:07 -- math precludes it.
Why minutes and seconds don´t check
Dalarid, August 29, 2004 - 9:52 pm UTC
The last record fails by 1 second. What's Wrong.
MOD.> select to_char( salida_plan, 'dd-mon-yyyy hh24:mi:ss' ) salida,
2 to_char( regreso_plan, 'dd-mon-yyyy hh24:mi:ss' ) regreso,
3 trunc( regreso_plan-salida_plan ) "Dy",
4 trunc( mod( (regreso_plan-salida_plan)*24, 24 ) ) "Hr",
5 trunc( mod( (regreso_plan-salida_plan)*24*60, 60 ) ) "Mi",
6 trunc( mod( (regreso_plan-salida_plan)*24*60*60, 60 ) ) "Sec"
7 from ordenes_embarque;
SALIDA REGRESO Dy Hr Mi Sec
-------------------- -------------------- ------- ------- ------- -------
27-aug-2004 12:14:00 27-aug-2004 15:40:00 0 3 26 0
27-aug-2004 11:27:00 27-aug-2004 14:25:00 0 2 58 0
27-aug-2004 14:55:00 27-aug-2004 17:33:00 0 2 38 0
27-aug-2004 14:00:00 27-aug-2004 18:09:00 0 4 9 0
27-aug-2004 14:00:00 27-aug-2004 17:52:00 0 3 52 0
27-aug-2004 14:00:00 27-aug-2004 16:13:00 0 2 13 0
27-aug-2004 16:43:00 27-aug-2004 17:43:00 0 1 0 0
27-aug-2004 14:00:00 27-aug-2004 17:38:00 0 3 38 0
27-aug-2004 10:42:00 27-aug-2004 11:44:00 0 1 1 59
August 30, 2004 - 8:11 am UTC
rounding error.
display months b/w a date range
Asim Naveed, September 01, 2004 - 3:44 am UTC
Hi,
I have a slightly different problem.
Create table t1 (d1 date not null, d2 date not null);
INSERT INTO T1
(d1, d2)
values
(
TO_DATE('04-JAN-2004','DD-MON-YYYY'),
TO_DATE('17-OCT-2004','DD-MON-YYYY')
)
I want to make a query that will display the month
names between d2 and d1, note that there can be more than
12 months b/w d2 and d1.
The prefered solutions is to do it with pure SQL and/or
built in functions but not user defined functions.
These month names can be
displayed in any of the follwing two ways
1- Concatenate all the month names and display it under
a new column, output like this
d1 d2 Months_between
--- --- ----------------
04-JAN-2004 17-OCT-2004 Jan,Feb,Mar,Apr...Oct
14-OCT-2003 12-dec-2003 Oct, Nov, Dec
.
.
2- The manth names can be displayed one on each row,
out put like this
d1 d2 Months_between
--- ---- ---------------
04-JAN-2004 17-OCT-2004 Jan
Feb
Mar
Apr
May
June
July
Aug
Sep
Oct
10-oct-2003 12-dec-2003 Oct
Nov
Dec
.
.
.
.
.
.
.
September 01, 2004 - 8:17 am UTC
ops$tkyte@ORA9IR2> select d1, d2, add_months(trunc(d1,'mm'),r)
2 from t1, (select rownum-1 r
3 from all_objects
4 where rownum <= ( select max(months_between(d2,d1))+1 from t1) )
5 where months_between(d2,d1)+1 >= r
6 /
D1 D2 ADD_MONTH
--------- --------- ---------
04-JAN-04 17-OCT-04 01-JAN-04
04-JAN-04 17-OCT-04 01-FEB-04
04-JAN-04 17-OCT-04 01-MAR-04
04-JAN-04 17-OCT-04 01-APR-04
04-JAN-04 17-OCT-04 01-MAY-04
04-JAN-04 17-OCT-04 01-JUN-04
04-JAN-04 17-OCT-04 01-JUL-04
04-JAN-04 17-OCT-04 01-AUG-04
04-JAN-04 17-OCT-04 01-SEP-04
04-JAN-04 17-OCT-04 01-OCT-04
10 rows selected.
You just need a table to join to that has more rows than "max months between"
display months b/w a date range
Asim Naveed, September 01, 2004 - 10:56 pm UTC
Wow, thats great, it almost solved my problem.
But I also want that d1,d2 values should not
repeat for every row (generated by joining). d1, d2
should only display once.
Also is there anyway I can display all month names
concatenated as I mentioned in output way no. 1. If not
possible with pure SQL and Builtin function, how is it
possible with User defined functions
Thanks alot for your help
September 02, 2004 - 8:02 am UTC
simple, instead of :
select d1, d2, ...
select decode( r, 0, d1 ), decode( r, 0, d2 ), ....
search for stragg for one concat method, or if you have a "reasonable" maximum, you can:
if you have a reasonable maximum (and you do, varchar2's are 4000 -- so you have a 1,000 max)
ops$tkyte@ORA9IR2> select d1, d2,
2 rtrim(
3 decode( sign( 0-months_between(d2,d1)), -1, to_char(add_months(d1,0),'mon')||',' ) ||
4 decode( sign( 1-months_between(d2,d1)), -1, to_char(add_months(d1,1),'mon')||',' ) ||
5 decode( sign( 2-months_between(d2,d1)), -1, to_char(add_months(d1,2),'mon')||',' ) ||
6 decode( sign( 3-months_between(d2,d1)), -1, to_char(add_months(d1,3),'mon')||',' ) ||
7 decode( sign( 4-months_between(d2,d1)), -1, to_char(add_months(d1,4),'mon')||',' ) ||
8 decode( sign( 5-months_between(d2,d1)), -1, to_char(add_months(d1,5),'mon')||',' ) ||
9 decode( sign( 6-months_between(d2,d1)), -1, to_char(add_months(d1,6),'mon')||',' ) ||
10 decode( sign( 7-months_between(d2,d1)), -1, to_char(add_months(d1,7),'mon')||',' ) ||
11 decode( sign( 8-months_between(d2,d1)), -1, to_char(add_months(d1,8),'mon')||',' ) ||
12 decode( sign( 9-months_between(d2,d1)), -1, to_char(add_months(d1,9),'mon')||',' ) ||
13 decode( sign(10-months_between(d2,d1)), -1, to_char(add_months(d1,10),'mon')||',' ) ||
.......
14 decode( sign(111-months_between(d2,d1)), -1, to_char(add_months(d1,111),'mon')||',' ),',') months
15 from ( select to_date('04-jan-04') d1, to_date( '17-oct-04') d2 from dual )
16 /
D1 D2 MONTHS
--------- --------- ------------------------------------------------
04-JAN-04 17-OCT-04 jan,feb,mar,apr,may,jun,jul,aug,sep,oct
Fiscal Year
Guna, October 21, 2004 - 7:46 pm UTC
Hi Tom, I have following requirments.
1 )I have a date column called pstart_date, I want to select a value like "pstart_date < (TODAY() 4 months)"
2 )I want to create a view based on multiple columns from a table and a column called FY (not exists in table), this FY column should return like "If TODAY() BETWEEN October 1 AND December 31, THEN FY = [YEAR of TODAY()] ELSE FY = [YEAR of TODAY()] - 1 "
3) I have a column called pend_date, I want to select a value like "BETWEEN October 1, ([FY]-1) and September 20, [FY] "
Please help me.
Thanks in advance,
Guna
October 22, 2004 - 3:53 pm UTC
1) what does it mean to you to "select that" column?
probably:
select ....,
case
when pstart_date < add_months(sysdate,-4)
then 'Yes, it is older than 4 months'
else 'Nope, not older than 4 months'
end,
.....
you can use decode as well
decode( sign( pstart_date - add_months(sysdate,-4) ), -1, "yes, it is older...',
'nope, not older...' )
2) see #1, you should be able to figure that out now, case or decode....
3) see #1
Clarificatins..
A reader, October 26, 2004 - 2:25 pm UTC
Tom, Thanks for your response.One more question,
If today's date is between Sep 01 2004 and December 31 2004, then I need Year of today +1 (ie 2005) else I need year of today - 1(ie 2003).
=====================================
Here is my query.
SELECT decode( sign( sysdate - add_months(sysdate,+2) ), -1,)
(select extract(year from sysdate+365) from dual),(select extract(year from sysdate) from dual ) FY from dual
/
I'm getting missing expression error.
Thanks.
October 26, 2004 - 2:47 pm UTC
1 select case when to_number(to_char(sysdate,'MM')) between 9 and 12
2 then to_char(add_months(sysdate,12),'yyyy')
3 else to_char(sysdate,'yyyy')
4 end
5* from dual
ops$tkyte@ORA9IR2> /
CASE
----
2005
Date
A reader, October 26, 2004 - 3:18 pm UTC
Thanks a lot Tom.Instead of just 2005 ,I need FY2005 as an output.Pls let me know.
October 26, 2004 - 3:28 pm UTC
use 'FY'||to_char(...,'yyyy') then.
Thank You !
A reader, October 26, 2004 - 3:38 pm UTC
Thank you very much Tom !
David Aldridge, October 26, 2004 - 4:38 pm UTC
>> use 'FY'||to_char(...,'yyyy') then
or to_char(...,'"FY"yyyy') of course. Not much difference here, but it's handy syntax to know if you need to generate something like "Y2004_M03_D07" with ...
to_char(...,'"Y"yyyy"_M"mm"_D"dd')
... instead of ...
'Y'||to_char(...,'yyyy')||'_M'||to_char(...,'mm')||'_D'||to_char(...,'dd')
Need Help !
A reader, October 27, 2004 - 8:02 pm UTC
SQL> create or replace view BUDGET_SPREADSHEET_VIEW as select g.grant_id,G.GRANTNUM,G.PROJSTART,g.pr
ojend, (select case when to_number(to_char(sysdate,'MM')) between 9 and 12 then 'FY'||to_char add_monthssysdate,12),'yyyy') else to_char sysdate,'yyyy') end FY from dual) as FY,U.LAST_NAME, U.FIRST_NAME,CI.contact_lname,CI.contact_COMPANY,GB.DIRCOST,GB.INDCOST,GB.PERIODST,decode(gb.type,36,'Conn',
39,'CptCont',
38,'Supp',
37,'Supp',
35,'Cont',
40,'New',
145,'NCE',
34,'Supp',
11101,'Supp') AWARD_STATUS
FROM GRANTS G,USERS U,CONTACT_INFO CI,GRANT_BUDGET GB
where
g.projend > sysdate and
g.projstart < sysdate and
g.applications_id=gb.applications_id and
u.user_id=g.po_user_id and
(ci.grant_id=g.applications_id and ci.contact_type=1 )and
gb.PERIODST between TO_DATE('01-OCT-2004') AND TO_DATE('30-SEP-2005')
View created.
Here, at the bottom of the where clause, I have hardcoded the column gb.PERIODST "TO_DATE('01-OCT-2004') AND TO_DATE('30-SEP-2005') instead of that I need to pass dynamic dates like following,
===================
instead of TO_DATE('01-OCT-2004') I need --- if month is between 9-12 then TO_DATE('01-OCT-yyyy(current year)') else yyyy-1
=======================
instead of TO_DATE('30-SEP-2005') I need --- if month is between 9-12 then I need TO_DATE('30-SEP-yyyy+1') else yyyy
I'm not sure how to achieve this on where clause.Please help me.
Thanks.
October 27, 2004 - 8:36 pm UTC
use case -- just like in the example above in the select from dual.
Need help
A reader, October 28, 2004 - 10:27 am UTC
Hi Tom,
I'm not sure how to use case in the where clause.I tried but no luck.Could you please show me how to do using same query.I would really appreicate your help.
October 28, 2004 - 1:44 pm UTC
where (case when <whatever> then <whatever value>
when <whatever> then <whatever value>
end) = <xxxx>
just use (case ... end) wherever you could use '42' for example (anywhere you would stick a constant, an expression, a string -- you can stick (case ... end)
A reader, October 28, 2004 - 2:21 pm UTC
Hi Tom,
Please see what I'm doing wrong.
create or replace view budget_test as select g.grant_id,G.GRANTNUM,G.PROJSTART,g.projend, (select case when to_number(to_char(sysdate,'MM')) between 9 and 12
then to_char(add_months(sysdate,12),'yyyy') else
to_char(sysdate,'yyyy') end FY from dual) as FY,U.LAST_NAME,U.FIRST_NAME,CI.contact_lname,CI.contact_COMPANY,GB.DIRCOST,GB.INDCOST,GB.PERIODST,decode(gb.type,36,'Conn',39,'CptCont',
38,'Supp',
37,'Supp',
35,'Cont',
40,'New',
145,'NCE',
34,'Supp',
11101,'Supp') AWARD_STATUS
FROM GRANTS G,USERS U,CONTACT_INFO CI,GRANT_BUDGET GB
where
g.projend > sysdate and
g.projstart < sysdate and
g.applications_id=gb.applications_id and
u.user_id=g.po_user_id and
(ci.grant_id=g.applications_id and ci.contact_type=1 )and
gb.PERIODST between TO_DATE(select case when to_number(to_char(gb.PERIODST,'MM')) between 9 and 12 then to_char(add_months(sysdate,12),'yyyy') else to_char(sysdate,-12),'yyyy') end )
AND TO_DATE((select case when to_number(to_char(gb.PERIODST,'MM')) between 9 and 12 then to_char(add_months(sysdate,12),'yyyy') else to_char(sysdate,-12),'yyyy') end )
===========================
October 28, 2004 - 7:34 pm UTC
i don't see any error messages?
A reader, October 29, 2004 - 8:13 am UTC
Hi Tom,
I'm getting expression missing error.I want to know that I'm following the syntax.
Like in the where clause, I mentioned periodst date between to_date(select case...)
Thanks.
October 29, 2004 - 8:56 am UTC
don't understand all of the to'ing and fro'ing going on -- but you want:
where ...
and COLUMN between A and B
A and B are your "case" statements -- your expressions.
and column between (case when to_char(gb.PERIODST,'MM') between 9 and 12
then to_char(add_months(sysdate,12),'yyyy')
else to_char(sysdate,'yyyy')
end)
and (the other case)
A reader, October 29, 2004 - 2:26 pm UTC
Hi Tom,
Please see below.
=======================
gb.PERIODST BETWEEN (CASE WHEN TO_CHAR(gb.PERIODST,'MM') BETWEEN 9 AND 12
THEN TO_CHAR(ADD_MONTHS(SYSDATE,12),'yyyy')
ELSE TO_CHAR(SYSDATE,'yyyy')
END)
AND (CASE WHEN TO_CHAR(gb.PERIODST,'MM') BETWEEN 9 AND 12
THEN TO_CHAR(ADD_MONTHS(SYSDATE,12),'yyyy')
ELSE TO_CHAR(SYSDATE,'yyyy') END)
SQL> /
View created.
SQL> select count(*) from testing;
select count(*) from testing
*
ERROR at line 1:
ORA-01843: not a valid month
October 29, 2004 - 5:04 pm UTC
drop the predicate from the view, does it still happen (eg: why do you believe it to be the predicate and is periodst a DATE or what type is it)
A reader, October 29, 2004 - 11:47 pm UTC
Hi Tom,
I don't understand what you mean by drop the pridicate.The column periodst is a date column.Thanks.;
October 30, 2004 - 2:10 pm UTC
predicate = where clause.
you are assuming the error comes because of the predicate, well, drop the predicate to test that theory.
all over the place ...
Gabe, November 01, 2004 - 12:55 pm UTC
To "A reader" ...
Your capacity for being clear and consistent is just terrible
<quote>
what I'm doing wrong?</quote>
else to_char(sysdate,-12),'yyyy') end
<== this is obviously wrong
<quote>select count(*) from testing;</quote>
If your view compiled then syntax is OK
hence the problem you have is about data <quote>ORA-01843: not a valid month</quote>
which should come to no surprise giving all the implicit type conversions you are doing all over the place: comparing strings to numbers, comparing dates to strings, converting strings to dates without a format mask.
Some people just cannot be helped
it is just a fact. In any case, if you still want some help then post a complete scenario
not just bits and pieces
you know, assume the readers of your stuff cannot read your mind and/or imagine your particular environment.
Using Greatest() to select highest Date
denni50, December 03, 2004 - 11:20 am UTC
Hi Tom
I'm modifying a procedure(not mine) and was wondering if
the Greatest Function can be used in a WHERE clause like
below:
update tablname set colA = 'Y'
where timestamp = (greatest(date_mrg,date_del))
and (id =delete_id or id=merge_id);
or does the Greatest() need to assign a value to a variable
like:
value:=greatest(date_mrg,date_del)....
reason for asking is the above update statement
is not updating colA based on the where condition.
thanks
December 03, 2004 - 1:20 pm UTC
greatest is just a function like upper, decode, whatever. it can be used like that.
thanks Tom...
denni50, December 03, 2004 - 1:32 pm UTC
did some testing to see how it works...since
I've never used it before.
(meaning Greatest Function).
less than 30 days
Otn, January 07, 2005 - 7:19 pm UTC
Create table t( date1 date, date2 date)
/
insert into t values ( sysdate, sysdate -1)
/
insert into t values ( sysdate, sysdate -40)
/
Tom
I need to query t for all those rows where the different between date1 and date2 is less than a month. Mind you some months have 30 days , some 31 and you know about FEB.
Please provide a solution.
Thanks
January 08, 2005 - 4:20 pm UTC
where months_between( date1, date2 )........
Otn
Otn, January 08, 2005 - 9:32 pm UTC
select count(*)
from t
where months_between(date1, date2) <1
I guess the above will give the answer for me.
Tom, now I want to group by month i.e. I want to display the number of rows where the months_between(date1, date2) <1, is grouped by each month of the year. The month should be taken from date1.
Will the below suffice
select to_char(date1, 'yyyymm'), count(*)
from t
where months_between(date1, date2) <1
group by to_char(date1, 'yyyymm')
January 09, 2005 - 11:25 am UTC
I'd use trunc(date1,'mm') but other than that, sure.
trunc(date1,'mm')
Otn, January 09, 2005 - 5:07 pm UTC
What will trunc(date1,'mm') ensure, why cant you use to_char ?
January 09, 2005 - 5:14 pm UTC
did I say you cannot use to_char?
I said:
I'd use trunc(date1,'mm') but other than that, sure.
^^^^^^^
Your goal is to truncate the date to the month level. Nothing does that faster than "trunc" (no nls_date routines, no conversion from date to string, etc).
That and "trunc(date1,'mm')" says explicitly "i am truncating the date to a month"
to_char( date1, 'yyyymm' ) says indirectly that, but mostly it says "we are making a string out of a date"
OK
John, January 10, 2005 - 12:26 pm UTC
Hi Tom,
What is the difference between the date formats
dd-mon-YYYY and dd-mon-RRRR?
SQL> select to_char(current_date,'dd-mon-YYYY') as Dt from dual
2 union all
3 select to_char(current_date,'dd-mon-RRRR') as Dt from dual
4 /
DT
-----------
10-jan-2005
10-jan-2005
January 10, 2005 - 1:58 pm UTC
in a to_char, no different.
in a to_date, different
ops$tkyte@ORA9IR2> alter session set nls_date_format = 'dd-mon-yyyy hh24:mi:ss';
Session altered.
ops$tkyte@ORA9IR2>
ops$tkyte@ORA9IR2> select to_date( '01-jan-05', 'dd-mon-yyyy' ),
2 to_date( '01-jan-05', 'dd-mon-rrrr' )
3 from dual
4 /
TO_DATE('01-JAN-05', TO_DATE('01-JAN-05',
-------------------- --------------------
01-jan-0005 00:00:00 01-jan-2005 00:00:00
^^ ^^
time component
dl, January 14, 2005 - 11:41 am UTC
Tom
I have read this thread with interest my problem relates to comparing time component. I would've thought this to be simple but am having difficulties:
drop table t1;
create table t1 (c1 number, c2 date);
insert into t1 values (1, sysdate - 10/4);
insert into t1 values (1, sysdate - 54/3);
insert into t1 values (1, sysdate - 53/6);
insert into t1 values (1, sysdate - 564/7);
insert into t1 values (1, sysdate - 10/2);
insert into t1 values (1, sysdate - 10/54);
select c1, c2 from t1;
C1 C2
---------- -------------------
1 12/01/2005 03:41:34
1 27/12/2004 15:41:34
1 05/01/2005 19:41:35
1 26/10/2004 01:58:45
1 09/01/2005 15:41:37
1 14/01/2005 11:14:58
Now what I want to do is look for all the rows which have dates in the morning, I tried :
select * from t1
where
c2 >= TO_DATE('00:00:00', 'HH24:MI:SS')
AND c2 <= TO_DATE('11:59:59', 'HH24:MI:SS')
but no rows.
What am i doing wrong?!
All i need to do is compare just the time component.
I also noticed that
16:35:42 OE9@DEV9>select sysdate from dual;
SYSDATE
-------------------
14/01/2005 16:35:38
1 row selected.
Elapsed: 00:00:00.00
16:37:13 OE9@DEV9>select TO_DATE('23:59:59', 'HH24:MI:SS')
16:37:14 2 from dual;
TO_DATE('23:59:59',
-------------------
01/01/2005 23:59:59
1 row selected.
when selecting just the time only from dual the date defaults to 01/01/2005 why is this?? This is the reason i think that my sql is not working.
How should i do this?
January 14, 2005 - 7:57 pm UTC
there you do have to apply a function to the date column, you cannot "between" or range on this without it.
where to_number(to_char(c2,'hh24')) between 0 and 11;
thanks but how about as a fraction
dxl, January 17, 2005 - 4:55 am UTC
1)
Thanks for that but i don't just want to compare one part of the time I need to be able to compare the whole time string down to the nearest second ie i want to be able to say
eg 11:19:57 is it bigger or smaller than another time 12:51:02
Do I have to separate each component and compare??
I am I correct in saying oracle stores the time as a fraction of a day eg 12:00:00 is 1/2 = 0.5 ??
If so how can i extract this fraction ? ie convert a full date string to its julian number including the fraction for the time?? Because then it would be easy to compare the 2 fractions.
2) also can you shed some light on why oracle defaulted the day to the 01/01/2005 :
I also noticed that
16:35:42 OE9@DEV9>select sysdate from dual;
SYSDATE
-------------------
14/01/2005 16:35:38
1 row selected.
Elapsed: 00:00:00.00
16:37:13 OE9@DEV9>select TO_DATE('23:59:59', 'HH24:MI:SS')
16:37:14 2 from dual;
TO_DATE('23:59:59',
-------------------
01/01/2005 23:59:59
seems a bit strange, why does it do that?
Many thanks
January 17, 2005 - 8:36 am UTC
use:
to_number(to_char(date,'hh24miss'))
and you'll find that 111957 is less than 125102
An Alternative?
John Spencer, January 17, 2005 - 11:06 am UTC
Tom:
Other than saving 3 characters typing, is there any benefit to:
WHERE TO_NUMBER(TO_CHAR(date,'sssss')) < 43200
over 'hh24miss'?
Thanks
January 17, 2005 - 11:16 am UTC
that works too.
to_char rules!; difference between 2 dates
Alf, February 04, 2005 - 1:05 pm UTC
Thanks Tom for this site I always find answers in here!
Hey Andrew! Thanks for method1
Exactly what I need for this:
s.logon_time "Connect Time", sysdate "Current Time",
to_char(trunc (sysdate-s.logon_time), '000') || ' days ' ||
to_char(trunc (sysdate) + mod (sysdate-s.logon_time,1),'HH24 "hrs" MI "min" SS "sec"') Session_Time_Connected
dfdfs
fssf, February 07, 2005 - 1:41 pm UTC
css
February 07, 2005 - 4:08 pm UTC
afdkl to you
DIFFERENCE PROBELM
ASIM, February 27, 2005 - 3:23 pm UTC
Hi Tom
I am facing a probelm, hope you will help..
I am using this fuction
Create Function date_diff ( p_date1 DATE, p_date2 DATE)
return char
is
Years NUMBER;
months NUMBER;
days NUMBER;
day_fraction NUMBER;
hrs NUMBER;
mints NUMBER;
sec NUMBER;
begin
Years :=trunc( months_between( p_date2 , p_date1 ) /12 );
months:=mod( trunc( months_between( p_date2, p_date1 ) ), 12 );
days :=trunc(p_date2 -
add_months(p_date1,trunc(months_between(p_date2,p_date1) )));
day_fraction:= (p_date2-p_date1)-trunc(p_date2-p_date1);
hrs :=trunc(day_fraction*24);
mints :=trunc((((day_fraction)*24)-(hrs))*60);
sec :=trunc(mod((p_date2-p_date1)*86400,60));
return(years||' Years '||months||' Months '||days||' Days '||hrs||' Hours '||mints||' Minutes & '||sec||' Seconds');
end;
and it is giving me this output
SQL> SELECT DATE_DIFF('13-MAR-1877', SYSDATE) FROM DUAL
/
DATE_DIFF('13-MAR-1877',SYSDATE)
--------------------------------------------------------------------------------
127 Years 11 Months 15 Days 1 Hours 15 Minutes & 49 Seconds
But when i do this
SQL>SELECT DATE_DIFF('13-MAR-1877 13:07:00', SYSDATE) FROM DUAL
/
IT GIVES ME THIS ERROR...
SELECT DATE_DIFF('13-MAR-1877 13:07:00', SYSDATE) FROM DUAL
*
ERROR at line 1:
ORA-01858: a non-numeric character was found where a numeric was expected
SINCE I WANT TO GET THE DIFFERENCE SINCE '15 MARCH 1877 13:07:00'
IN THE FORMAT OF
127 Years 11 Months 15 Days ? Hours ? Minutes & ? Seconds
SO WHAT SHOULD I NEED TO DO TO GET THIS..
REGARDS,
ASIM.
February 27, 2005 - 3:51 pm UTC
when you have a string, you need to convert the string to a date.
to_date( '13-mar-1887 13:07:00', 'dd-mon-yyyy hh24:mi:ss' )
is what you want. And likewise when retrieving a date into a string -- you want to use the appropriate format on the way out as well
Date Splits
Susan, February 28, 2005 - 4:48 am UTC
Dear Tom,
I want output to be like below. It is possible to achieve it in a single query?
StartDate EndDate Days
04-01-2004 31-01-2004 28
01-02-2004 29-02-2004 29
01-03-2004 31-03-2004 31
----
----
----
01-10-2004 17-10-2004 17
Thanks
Susan
Date Splits
Susan, February 28, 2005 - 5:11 am UTC
I was able to work out based on the examples given in this
thread. Can this be improved ?
select d1, d2, greatest(add_months(trunc(d1,'mm'),r),d1) startdate
,least(last_day(add_months(trunc(d1,'mm'),r)),d2) enddate,
(( least(last_day(add_months(trunc(d1,'mm'),r)),d2)
- greatest(add_months(trunc(d1,'mm'),r),d1) ) + 1 )days
from t1, (select rownum-1 r
from all_objects
where rownum <= ( select max(months_between(d2,d1))+1 from t1)
)
where months_between(d2,d1)+1 >= r
Thanks,
Susan
February 28, 2005 - 7:57 am UTC
not clear what the inputs are here --
is the problem "given two dates, produce a row for each month"
Date Splits
Susan, February 28, 2005 - 5:45 pm UTC
Sorry for not being clear. "Problem is given two dates,
produce a row for each month with start date, end date ,
and number of days in each month displayed".
In example given, first row should have actual start date of 04/01/2004
and last row should have actual end date of 17/10/2004.
Please suggest whether query can be improved.
StartDate EndDate Number of Days
4/01/2004 31/01/2004 28
1/02/2004 29/02/2004 29
1/03/2004 31/03/2004 31
1/04/2004 30/04/2004 30
1/05/2004 31/05/2004 31
1/06/2004 30/06/2004 30
1/07/2004 31/07/2004 31
1/08/2004 31/08/2004 31
1/09/2004 30/09/2004 30
1/10/2004 17/10/2004 17
Create table t1 (d1 date not null, d2 date not null);
INSERT INTO T1
(d1, d2)
values
(
TO_DATE('04-JAN-2004','DD-MON-YYYY'),
TO_DATE('17-OCT-2004','DD-MON-YYYY')
)
select d1, d2, greatest(add_months(trunc(d1,'mm'),r),d1) startdate
,least(last_day(add_months(trunc(d1,'mm'),r)),d2) enddate,
(( least(last_day(add_months(trunc(d1,'mm'),r)),d2)
- greatest(add_months(trunc(d1,'mm'),r),d1) ) + 1 )days
from t1, (select rownum-1 r
from all_objects
where rownum <= ( select max(months_between(d2,d1))+1 from t1)
)
where months_between(d2,d1)+1 >= r
Thanks,
Susan.
March 01, 2005 - 8:59 am UTC
that looks fine actually -- same approach i would take.
Generate the set of rownums (numbers 1, 2, ... N) to cover the months and then fetch out the right start/stop for each month.
OK
Siva, March 02, 2005 - 10:28 am UTC
i Tom,
Is it possible to format the date that we enter and store it in
the table? Please see below.
SQL> create table t(
2 x date default to_char(x,'mm/dd/yyyy')
3 )
4 /
x date default to_char(x,'mm/dd/yyyy')
*
ERROR at line 2:
ORA-00984: column not allowed here
Any workaround for this?
Please do reply.
March 02, 2005 - 11:01 am UTC
to_char converts a date to a string.
to_date converts a string to a date.
a format is applied to a DATE variable upon retrieval to present it to you in some appealing format.
It is not used to "store" the data.
create table t ( x date );
select to_char(x,'mm/dd/yyyy') x from t;
is what you are looking for. dates are simply a 7 byte field that stores
YY century
YY year
MM month
DD day
HH24 hour
MI minutes
SS seconds.
if you are trying to make sure that the date as inserted does not have a time component,
...
x date check (x = trunc(x,'dd')),
...
would verify programs are inserting the right stuff, or
create trigger....
begin
:new.x := trunc(:new.x,'dd');
end;
/
would silently remove any time component
but you would STILL use to_char on the way out to format it pretty.
Getting sysdate from another machine
Aditya Saraogi, June 21, 2005 - 5:57 am UTC
Hi Tom,
I want to retrieve the sysdate from another machine (say B).
Something like:
select sysdate from dual@db_link_name;
This obviously does not work. How can I then retrieve the sysdate from another database (while being connected to another DB), the condition being that I cannot create any object on the target machine.
Thanks
June 21, 2005 - 4:43 pm UTC
use dbms_sql to run a query on the remote site.
dbms_sql@remote_site.parse( 'begin :x := sysdate; end;' )
use dbms_sql@remote_site to bind and execute and open and all.
(I find it easiest to set up a private synonym for dbms_sql@remote_site)
DB Link error
Aditya Saraogi, June 22, 2005 - 7:37 am UTC
Hi Tom,
I had tried to implement your idea. However I am not very conversant with the DBMS_SQL package and I am not sure how to use it, to obtain the required output.
The DBLink was created as
CREATE DATABASE LINK testravi
CONNECT TO SCOTT IDENTIFIED BY tiger
USING 'ravindra';
I wrote the following anonymous block
declare
var_date DATE;
begin
var_date := sysdate;
dbms_sql@testravi.parse('begin :var_date := sysdate; end;');
dbms_output.put_line(to_char(var_date,'DD MM YYYY HH24:MI:SS'));
end;
The error thrown out was as following:
SQL> show user;
USER is "DARTTEST"
SQL> declare
2 var_date DATE;
3 begin
4 var_date := sysdate;
5 dbms_sql@testravi.parse('begin :var_date := sysdate; end;');
6 dbms_output.put_line(to_char(var_date,'DD MM YYYY HH24:MI:SS'));
7 end;
8 /
declare
*
ERROR at line 1:
ORA-04054: database link TESTRAVI.PARSE does not exist
Please advise.
Thanks
Aditya Saraogi
June 22, 2005 - 1:53 pm UTC
search for dbms_sql on this site, tons of examples. There is documentation on the supplied packages as well with examples.
You'll want to use the synonym I described to make life "easier"
Creating Synonyms in the Procedure
Aditya Saraogi, July 06, 2005 - 2:32 am UTC
Hi Tom,
I was able to implement the dbms_sql@i_dblink concept by using a synonym. Thanks!
My requirement necessiates creating multiple db links and get the system time after connecting to the databases. How ever I am not able to dynamically create a synonym for dbms_sql@dblink in the stored procedure.
The error I get is
ORA-01031: insufficient privileges
I do have the necessary privileges as it is possible for me to create the synonym from the regular SQL Plus prompt.
The procedure written by me is as following:
CREATE OR REPLACE PROCEDURE getSysdate
(
i_dblink IN dblinks.dblink%type
)
IS
l_var_date DATE;
l_cursor_name NUMBER;
l_rows_processed NUMBER;
l_str VARCHAR2(200);
BEGIN
l_str := 'CREATE SYNONYM db_s for dbms_sql@'||i_dblink;
execute immediate l_str;
END getSysdate;
Thanks,
Aditya Saraogi
July 06, 2005 - 7:47 am UTC
What am i doing wrong?
Aditya Saraogi, July 06, 2005 - 7:36 am UTC
Hi Tom,
I was able to rectify the previous problem by using the AUTHID Current_User construct, but I am not able to get why should a privilege conflict arise when both the onwer and user of the procedure are same?
I had developed on the previous procedure to get the following sp:
CREATE OR REPLACE PROCEDURE getSysdate
(
i_dblink IN dblinks.dblink%type
,o_result OUT DATE
)
AUTHID CURRENT_USER
IS
l_var_date DATE;
l_cursor_name NUMBER;
l_rows_processed NUMBER;
l_str VARCHAR2(200);
BEGIN
l_str := 'CREATE OR REPLACE SYNONYM l_synonym for sys.dbms_sql@'||i_dblink;
execute immediate l_str;
l_cursor_name := l_synonym.open_cursor;
l_synonym.parse(l_cursor_name,'SELECT SYSDATE FROM DUAL',dbms_sql.NATIVE);
l_synonym.DEFINE_COLUMN(l_cursor_name,1,l_var_date);
l_rows_processed := l_synonym.EXECUTE(l_cursor_name);
loop
exit when ( l_synonym.fetch_rows(l_cursor_name) <= 0);
l_synonym.COLUMN_VALUE(l_cursor_name,1,l_var_date);
end loop;
o_result := l_var_date;
l_synonym.CLOSE_CURSOR(l_cursor_name);
END getSysdate;
/
As you can see, I have to first explicitly define the synonym, l_synonym, and then only can I successfully compile the procedure.
After doing that when I try to execute the stored procedure with different values for i_dblink, the procedure always returs the time from the database whose dblink I had initially used to create the synonym(prior to the compilation of the procedure).
I checked the definition of the synonym after every test run of the procedure, and the definition of the synonym changed in accordance to the db_link provided by me, but still the time being returned was sourced from the same database.
Could you please tell me where am I erring ?
Thanks
Aditya Saraogi
July 06, 2005 - 8:23 am UTC
chicken + egg, which comes first.
you create the synonym, the procedure is going to go invalid. but you are running said procedure.
ops$tkyte-ORA10G> create or replace function get_remote_date( p_syn in varchar2 ) return date
2 as
3 l_the_date date;
4 begin
5
6 execute immediate replace( '
7 declare
8 l_cursor integer;
9 l_date date;
10 begin
11 l_cursor := DBMS_SQL.open_cursor@$SYN$;
12
13 dbms_sql.parse@$SYN$
14 ( l_cursor,
15 ''select sysdate d from dual'',
16 dbms_sql.native );
17
18 dbms_sql.define_column@$SYN$
19 ( l_cursor, 1, l_date );
20
21 if ( dbms_sql.execute_and_fetch@$SYN$
22 ( l_cursor, TRUE ) != 1
23 )
24 then
25 raise_application_error( -20001, ''failed'' );
26 end if;
27
28 dbms_sql.column_value@$SYN$( l_cursor, 1, l_date );
29
30 :x := l_date;
31 end;
32 ', '$SYN$', p_syn ) using IN OUT l_the_date;
33
34 return l_the_date;
35
36 end;
37 /
Function created.
ops$tkyte-ORA10G>
ops$tkyte-ORA10G> exec dbms_output.put_line( get_remote_date( 'ora9ir2' ) );
06-JUL-05
PL/SQL procedure successfully completed.
AUTHID and Autocompile
Aditya Saraogi, July 07, 2005 - 12:50 am UTC
Hi Tom,
Really cool solution. Goes to show the essenital ingredient of clear thinking in solving a problem. I was focussing on creating a synonym rather than getting the system date!
I have two doubts
1. The synonym in my procedure was being recreated during the execution of the procedure. Then why was it that even in the next run, it seemed as if the initial definition was being used to return the date.
2. Why was I not allowed to create a synonym in my own procedure, without first using the AUTHID CURRENT_USER construct?
For instance as a user say SCOTT, I am able to create the synonym via SQL PLUS. On the same schema I create a procedure which runs an execute immediate command carrying the instructions for creating a synonym. I run the procedure as SCOTT and the error is thrown. Why is AUTHID CURRENT_USER required?
Thank You
Aditya Saraogi
Thanks
Aditya Saraogi, July 09, 2005 - 1:57 am UTC
Hi Tom,
Thanks for the link . I had missed the link altogether in your previous reply. Sorry about that. Have recently purchased your book, Expert 1 on 1. Its reading is going to be like the dessert after a meal!
Thanks
Aditya Saraogi
30 days per month
A reader, August 08, 2005 - 9:21 am UTC
Our accounting rule is that all months have 30 days.
Given this rule, how can I calculate the number of days between 2 dates?
For example
ALTER SESSION SET nls_date_format='mm/dd/yyyy'
SELECT TO_DATE('4/30/2005')-TO_DATE('7/12/2004')+1 FROM dual
gives me 293
but if I apply the rule above (30 days per month), I get 288.
How can I modify my query to get 288?
Thanks
August 08, 2005 - 7:48 pm UTC
I have no idea how to deal with that accounting rule.
but if you use months_between....
I don't get 288.
ops$tkyte@ORA9IR2> SELECT months_between( TO_DATE('4/30/2005','mm/dd/yyyy'),TO_DATE('7/12/2004','mm/dd/yyyy')+1) FROM dual;
MONTHS_BETWEEN(TO_DATE('4/30/2005','MM/DD/YYYY'),TO_DATE('7/12/2004','MM/DD/YYY
-------------------------------------------------------------------------------
9.5483871
ops$tkyte@ORA9IR2> SELECT 30*months_between( TO_DATE('4/30/2005','mm/dd/yyyy'),TO_DATE('7/12/2004','mm/dd/yyyy')+1) FROM dual;
30*MONTHS_BETWEEN(TO_DATE('4/30/2005','MM/DD/YYYY'),TO_DATE('7/12/2004','MM/DD/
-------------------------------------------------------------------------------
286.451613
so I really don't know the logic you used (and once you write it down, the function probably falls right out on the paper :)
A reader, August 08, 2005 - 9:22 pm UTC
288 as in
SELECT 18 + 30*months_between(
trunc(TO_DATE('4/30/2005','mm/dd/yyyy'),'mm'),
trunc(TO_DATE('7/12/2004','mm/dd/yyyy'),'mm')) FROM
dual;
where 18 is 30-12 (the 18 days left in July 2004)
Something like that?
August 09, 2005 - 9:15 am UTC
but what happens if it was 4/12/2005 and 7/30/2004
I'd like the orginal poster to define the algorithm to be used (and once they do that, creating the function will be "simple")
difference between 2 dates
Geo, August 10, 2005 - 9:24 am UTC
DECLARE
end_date DATE := '31-MAR-1996';
start_date DATE := '28-FEB-1994';
start_date_lastday DATE;
end_date_firstday DATE;
months_num PLS_INTEGER;
years_num PLS_INTEGER;
days_num PLS_INTEGER;
BEGIN
end_date_firstday := LAST_DAY (ADD_MONTHS (end_date, -1));
start_date_lastday := LAST_DAY (start_date);
months_num := MONTHS_BETWEEN (end_date_firstday, start_date_lastday);
IF end_date = last_day(end_date) THEN
months_num := months_num + 1;
end_date_firstday := end_date;
END IF;
years_num := TRUNC (months_num) / 12;
months_num := months_num - (years_num * 12);
days_num := (start_date_lastday - start_date) + (end_date - end_date_firstday);
DBMS_OUTPUT.put_line (years_num || ' years, ' || months_num || ' months, ' || days_num || ' days');
END;
August 10, 2005 - 10:34 am UTC
<quote from a book being released soon, if the link
http://asktom.oracle.com/Misc/DateDiff.html didn't do it for you, this is the 9i and above easier way>
Getting the Difference Between Two DATEs
Another frequently asked question is, How do I retrieve the difference between two dates? The answer is deceptively simple: you just subtract them. This will return a number representing the number of days between the two dates.
Additionally, you have the built-in function MONTHS_BETWEEN that will return a number representing the number of monthsincluding fractional monthsbetween two dates. Lastly, with the INTERVAL types, you have yet another method to see the elapsed time between two dates. The following SQL query demonstrates the outcome of subtracting two dates (showing the number of days between them), using the MONTHS_BETWEEN function and then the two functions used with INTERVAL types:
ops$tkyte@ORA10G> select dt2-dt1 ,
2 months_between(dt2,dt1) months_btwn,
3 numtodsinterval(dt2-dt1,'day') days,
4 numtoyminterval(months_between(dt2,dt1),'month') months
5 from (select to_date('29-feb-2000 01:02:03','dd-mon-yyyy hh24:mi:ss') dt1,
6 to_date('15-mar-2001 11:22:33','dd-mon-yyyy hh24:mi:ss') dt2
7 from dual )
8 /
DT2-DT1 MONTHS_BTWN DAYS MONTHS
---------- ----------- ------------------------------ -------------
380.430903 12.5622872 +000000380 10:20:30.000000000 +000000001-00
Those are all correct values, but not of great use to us yet. Most applications would like to display the years, months, days, hours, minutes, and seconds between the dates. Using a combination of the preceding functions, we can achieve that goal. Well select out two intervals: one for the years and months, and the other for just the day, hours, and so on. Well use the MONTHS_BETWEEN built-in function to determine the decimal number of months between the two dates, and then well use the NUMTOYMINTERVAL built-in function to convert that number into the years and months. Additionally, well use MONTHS_BETWEEN to subtract the integer number of months between the two dates from the larger of the two dates to get down to the days and hours between them:
ops$tkyte@ORA10G> select numtoyminterval
2 (months_between(dt2,dt1),'month')
3 years_months,
4 numtodsinterval
5 (dt2-add_months( dt1, trunc(months_between(dt2,dt1)) ),
6 'day' )
7 days_hours
8 from (select to_date('29-feb-2000 01:02:03','dd-mon-yyyy hh24:mi:ss') dt1,
9 to_date('15-mar-2001 11:22:33','dd-mon-yyyy hh24:mi:ss') dt2
10 from dual )
11 /
YEARS_MONTHS DAYS_HOURS
--------------- ------------------------------
+000000001-00 +000000015 10:20:30.000000000
Now it is clear that there is 1 year, 15 days, 10 hours, 20 minutes, and 30 seconds between the two DATEs.
Beautiful! Thats very helpful.
A reader, August 10, 2005 - 11:18 am UTC
Difference of 30 mintues does not return 30 but 29
Juan Velez, August 29, 2005 - 9:24 pm UTC
select trunc(mod((to_date('200508291930','yyyymmddhh24mi')-
to_date('200508291900','yyyymmddhh24mi')
)* 24*60,60))
from dual
returns 29 not 30.
The same applying simple date arithmetic
select (
to_date('200508291930','yyyymmddhh24mi')-
to_date('200508291900','yyyymmddhh24mi')
) * 1440
from dual
returns 29.9999999999999999999999999995 not 30
Why? How can this be avoided? It also happens for 15, 5, 10, etc differences. It never returns the integer value. And sometimes to get the integer value you may need to use round or trunc. Not really deterministic.
What am I missing?
Thank you,
Juan A.
August 30, 2005 - 1:46 am UTC
adding 0.000001 is an option to correct for this - the repeating number is a problem (it repeats 33333333333333333 forever)
Difference of 30 mintues does not return 30 but 29
Juan Velez, September 08, 2005 - 6:21 pm UTC
My question was more towards asking why Oracle does not return an integer value. The problem is not only for 30 minutes but many other ranges. I imagine the only way to fix this, is by creating a function that returns the correct value(I think it only works in 9i and 10g as it requires timestamp). How could it have been done prior to timestamp?
FUNCTION GetNumberOfMinutes(
pDate1 IN DATE,
pDate2 IN DATE)
RETURN NUMBER DETERMINISTIC IS
x TIMESTAMP := CAST(pDate1 AS TIMESTAMP);
y TIMESTAMP := CAST(pDate2 AS TIMESTAMP);
BEGIN
RETURN (EXTRACT(DAY FROM (X-Y) )*24*60*60+
EXTRACT(HOUR FROM (X-Y) )*60*60+
EXTRACT(MINUTE FROM (X-Y) )*60+
EXTRACT(SECOND FROM (X-Y) ))/60;
END;
September 08, 2005 - 6:33 pm UTC
you are dealing with numbers that basically have 5 things after the decimal place, rounding happens.
Could you expand your explanation?
Juan Velez, September 08, 2005 - 9:38 pm UTC
Could you expand "you are dealing with numbers that basically have 5 things after the decimal
place, rounding happens."?
I would appreciate and like to learn why this happens. I thought Oracle treated dates differently when doing arithmetic between them.
I already fixed my problem but wanted to know why this happens.
Thank you,
JA
September 09, 2005 - 6:39 am UTC
sorry -- shouldn't have used 5, but basicallly we have a repeating number that would require a datatype with infinite precision to represent:
ops$tkyte@ORA10G> set numformat 9.999999999999999999999999999999999999
ops$tkyte@ORA10G> select (1/24/60/60)*30 from dual;
(1/24/60/60)*30
---------------------------------------
.000347222222222222222222222222222222
ops$tkyte@ORA10G>
ops$tkyte@ORA10G> select to_date('01-jan-2005 00:00:30','dd-mon-yyyy hh24:mi:ss')
2 -to_date('01-jan-2005 00:00:00','dd-mon-yyyy hh24:mi:ss')
3 from dual;
TO_DATE('01-JAN-200500:00:30','DD-MON-YYYYHH24:MI:SS')-TO_DATE('01-JAN-200500:0
-------------------------------------------------------------------------------
.000347222222222222222222222222222222
we don't have infinite precision, we have 38 digits, hence when we multiply back by 1440:
ops$tkyte@ORA10G> select (1/24/60/60)*(24*60) from dual;
(1/24/60/60)*(24*60)
---------------------------------------
.016666666666666666666666666666666667
we got yet another "infinitely long" number.
rounding occurs.
Difference in minutes betwee 2 dates
Juan Velez, September 13, 2005 - 9:45 pm UTC
Thanks for your explanation. Now I am wondering how would you have created a function in 8i and before that returned the difference in minutes between 2 dates?
JA
September 14, 2005 - 7:53 am UTC
fudge factor, just add 0.000001 to fudge it up.
<quote>boundary values, gotta hate them...</quote>
Duke Ganote, October 10, 2005 - 5:31 pm UTC
I just got nicked by ADD_MONTHS when the date is the last day of the February before a leap year:
with X as
( select TO_DATE('2003-FEB-28','YYYY-MON-DD') AS Feb_28_2003
from dual )
select TO_CHAR( Feb_28_2003,
'YYYY-MON-DD') AS "2003-FEB-28"
, TO_CHAR( add_months( Feb_28_2003, +12),
'YYYY-MON-DD') AS "2004-FEB-??"
from X
/
2003-FEB-28 2004-FEB-??
----------- -----------
2003-FEB-28 2004-FEB-29
ADD_MONTHS implicitly assumes you want the end of month.
October 10, 2005 - 7:36 pm UTC
add_months is DOCUMENTED to return the last day of the month in question when you add a month to the last day of a month.
Happens for all the months - add one month to sept 30th.
Read the (OTN) Manual
Duke Ganote, October 10, 2005 - 9:11 pm UTC
Gary
A reader, October 14, 2005 - 7:43 am UTC
Tom
I have a date table that has data like
date_pk
20050101
20050102
20050103
..
..
The date_pk is unique in the table..
I need a sql that can give the follwing output (range of 5)
20050101 20050105
20050106 20050110
20050111 20050115
..
...
Is it possible?
Thanks
October 14, 2005 - 9:01 am UTC
ops$tkyte@ORA10G> create table t
2 as
3 select to_number( to_char( to_date('01012005','ddmmyyyy')+rownum-1, 'yyyymmdd' ) ) x
4 from all_users;
Table created.
didn't know if you really mean "5" or "5 within a month"...
so, I'll give you "5 within a month" first.
ops$tkyte@ORA10G> select yyyy, mm, min(dd), max(dd)
2 from (
3 select trunc(x/10000) yyyy, mod( trunc(x/100), 100 ) mm, mod(x,100) dd
4 from t
5 )
6 group by yyyy, mm, trunc( (dd-0.01)/5 )
7 /
YYYY MM MIN(DD) MAX(DD)
---------- ---------- ---------- ----------
2005 1 1 5
2005 1 6 10
2005 1 11 15
2005 1 16 20
2005 1 21 25
2005 1 26 30
2005 1 31 31
2005 2 1 2
8 rows selected.
Or, if you just wanted "up to 5 rows grouped together after sorting"
ops$tkyte@ORA10G> select min(x), max(x)
2 from (
3 select x, rownum r
4 from (
5 select x
6 from t
7 order by x
8 )
9 )
10 group by trunc((r-0.01)/5);
MIN(X) MAX(X)
---------- ----------
20050101 20050105
20050106 20050110
20050111 20050115
20050116 20050120
20050121 20050125
20050126 20050130
20050131 20050202
7 rows selected.
Gary
A reader, October 14, 2005 - 9:24 am UTC
This is absolutely amazing...The trunc((r-0.01)/5) trick does the job real nice...Man..you are good
weeks
Tom, November 01, 2005 - 9:21 am UTC
Is it possible to achive the following thing..
we store weeks in our date dimension tables as 200301,200302...200352...
I am trying to find the difference between 2 weeks
like 200304 - 200301..here its 3 (direct difference)..but what incase its 200401 - 200351....??
any ideas
thanks
November 01, 2005 - 11:12 am UTC
my idea
is ALWAYS TO USE A DATE FOR A DATE
and a number to store - a number..............
and nothing else.
looks like you have dates here.
what function using to_date/substr/whatever would you use to convert your number into an Oracle date - supply that, and then we can subtract and divide by 7.
(eg: how many weeks does a year have - unfortunately, it is not constant typically, but it really utterly depends on your definition of when the weeks begin and end - I've no idea what date 200301 is...
1 select to_char(to_date('31-dec-'||r),'yyyyiw'),
2 to_char(to_date('01-jan-'||(r+1)),'yyyyiw')
3* from (select level r from dual connect by level <= 10 )
ops$tkyte@ORA10GR2> /
TO_CHA TO_CHA
------ ------
200101 200201
200201 200301
200301 200401
200453 200553
200552 200652
200652 200701
200701 200801
200801 200901
200953 201053
201052 201152
10 rows selected.
Now, if a week is just every seven days starting from 01-jan, you can always just take
ops$tkyte@ORA10GR2> select trunc(to_date( '2005', 'yyyy' ),'y')+5*7 from dual;
TRUNC(TO_
---------
05-FEB-05
your yyyy portion, trunc it, add you DD*7 portion - and do that to both "dates" and subtract.
A reader, November 01, 2005 - 12:43 pm UTC
Thanks for the update
The problem is we follow a fiscal calendar
say year is 2005
We have 52 weeks(200501..200552),12 months (200501--200512) ,2 seasons(20051,20052) , 4quarters(20051,20052,20053,20054) .i have pasted a sample data for the first quarter
what i need is to find the number of weeks between 200510 and 200504 ..like 6..WK--week,FY -fiscal,st -start,ed -end
FY_DATE WK_ST_DATE WK_ED_DATE FY_WK FY_MONTH
20050130 20050130 20050205 200501 200501
20050131 20050130 20050205 200501 200501
20050201 20050130 20050205 200501 200501
20050202 20050130 20050205 200501 200501
20050203 20050130 20050205 200501 200501
20050204 20050130 20050205 200501 200501
20050205 20050130 20050205 200501 200501
20050206 20050206 20050212 200502 200501
20050207 20050206 20050212 200502 200501
20050208 20050206 20050212 200502 200501
20050209 20050206 20050212 200502 200501
20050210 20050206 20050212 200502 200501
20050211 20050206 20050212 200502 200501
20050212 20050206 20050212 200502 200501
20050213 20050213 20050219 200503 200501
20050214 20050213 20050219 200503 200501
20050215 20050213 20050219 200503 200501
20050216 20050213 20050219 200503 200501
20050217 20050213 20050219 200503 200501
20050218 20050213 20050219 200503 200501
20050219 20050213 20050219 200503 200501
20050220 20050220 20050226 200504 200501
20050221 20050220 20050226 200504 200501
20050222 20050220 20050226 200504 200501
20050223 20050220 20050226 200504 200501
20050224 20050220 20050226 200504 200501
20050225 20050220 20050226 200504 200501
20050226 20050220 20050226 200504 200501
20050227 20050227 20050305 200505 200502
20050228 20050227 20050305 200505 200502
20050301 20050227 20050305 200505 200502
20050302 20050227 20050305 200505 200502
20050303 20050227 20050305 200505 200502
20050304 20050227 20050305 200505 200502
20050305 20050227 20050305 200505 200502
20050306 20050306 20050312 200506 200502
20050307 20050306 20050312 200506 200502
20050308 20050306 20050312 200506 200502
20050309 20050306 20050312 200506 200502
20050310 20050306 20050312 200506 200502
20050311 20050306 20050312 200506 200502
20050312 20050306 20050312 200506 200502
20050313 20050313 20050319 200507 200502
20050314 20050313 20050319 200507 200502
20050315 20050313 20050319 200507 200502
20050316 20050313 20050319 200507 200502
20050317 20050313 20050319 200507 200502
20050318 20050313 20050319 200507 200502
20050319 20050313 20050319 200507 200502
20050320 20050320 20050326 200508 200502
20050321 20050320 20050326 200508 200502
20050322 20050320 20050326 200508 200502
20050323 20050320 20050326 200508 200502
20050324 20050320 20050326 200508 200502
20050325 20050320 20050326 200508 200502
20050326 20050320 20050326 200508 200502
20050327 20050327 20050402 200509 200502
20050328 20050327 20050402 200509 200502
20050329 20050327 20050402 200509 200502
20050330 20050327 20050402 200509 200502
20050331 20050327 20050402 200509 200502
20050401 20050327 20050402 200509 200502
20050402 20050327 20050402 200509 200502
20050403 20050403 20050409 200510 200503
20050404 20050403 20050409 200510 200503
20050405 20050403 20050409 200510 200503
20050406 20050403 20050409 200510 200503
20050407 20050403 20050409 200510 200503
20050408 20050403 20050409 200510 200503
20050409 20050403 20050409 200510 200503
20050410 20050410 20050416 200511 200503
20050411 20050410 20050416 200511 200503
20050412 20050410 20050416 200511 200503
20050413 20050410 20050416 200511 200503
20050414 20050410 20050416 200511 200503
20050415 20050410 20050416 200511 200503
20050416 20050410 20050416 200511 200503
20050417 20050417 20050423 200512 200503
20050418 20050417 20050423 200512 200503
20050419 20050417 20050423 200512 200503
20050420 20050417 20050423 200512 200503
20050421 20050417 20050423 200512 200503
20050422 20050417 20050423 200512 200503
20050423 20050417 20050423 200512 200503
20050424 20050424 20050430 200513 200503
20050425 20050424 20050430 200513 200503
20050426 20050424 20050430 200513 200503
20050427 20050424 20050430 200513 200503
20050428 20050424 20050430 200513 200503
20050429 20050424 20050430 200513 200503
20050430 20050424 20050430 200513 200503
Thanks
Tom
November 02, 2005 - 4:50 am UTC
give me the function that converts your YYYYWW into a date.
actually, once you do that - you can do it yourself :)
A reader, November 03, 2005 - 1:57 pm UTC
That was funny :)
I know i cant do that..but i was thinking something like joining the week back to the table to grab the min or max of the week start date and then using a function ...
either ways,
Is there a weeks_between function in oracle?
November 04, 2005 - 2:47 am UTC
weeks_between := trunc( (dt1-dt2)/7 )
Problem with using add_months and Months_Between
Anandkumar, November 10, 2005 - 6:28 am UTC
Dear Tom ,
Try these two SELECTs statements !
select
months_between('28.03.2003','28.02.2003') Mnth_Bet1 ,
months_between('29.03.2003','28.02.2003') Mnth_Bet2 ,
months_between('30.03.2003','28.02.2003') Mnth_Bet3 ,
months_between('31.03.2003','28.02.2003') Mnth_Bet4
from dual
/
May be we should not use month_betweens to calculate days .
Using add_months could be have the potentual problem too .
select
rownum ,
Start_Date ,
add_months(Start_Date,rownum) Add_Start_Date ,
add_months(Start_Date,rownum) - Start_Date Diff_Start_Date ,
Mid_Date ,
add_months(Mid_Date,rownum) Add_Mid_Date ,
add_months(Mid_Date,rownum) - Mid_Date Diff_Mid_date ,
End_Date ,
add_months(End_Date,rownum) End_Date ,
add_months(End_Date,rownum) - End_Date Diff_End_Date
from ( select
rownum , to_date('28.02.2003') Start_Date , to_date('29.09.2003') Mid_Date , to_date('30.09.2003') End_Date
from dual connect by level < 13)
/
Tom , Correct me if I'am wrong !
November 11, 2005 - 11:44 am UTC
you cannot use months to compute days.
Think about it - answer this:
how many days are in a month?
You cannot answer that.
If you want to get months between use months_between. If you want days between, just subtract.
where is problem ?
parag jayant patankar, May 10, 2006 - 9:01 am UTC
Hi Tom,
I have used following your query to find out duration
1 select start_date, end_date, months_between( end_date, start_date ) x1,
2 trunc( months_between( end_date, start_date ) /12 ) Years,
3 mod( trunc( months_between( end_date, start_date )), 12 ) months,
4 greatest(0, (end_date - add_months(start_date,trunc( months_between(end_date, start_date))))
5 from ( select to_date('20060531', 'RRRRMMDD') end_date,
6 to_date('20060228', 'RRRRMMDD') start_date
7 from dual
8* )
18:41:50 SQL> /
START_DAT END_DATE X1 YEARS MONTHS DAYS
--------- --------- ---------- ---------- ---------- ----------
28-FEB-06 31-MAY-06 3 0 3 0
According to me it should be 3 months 3 days. Kindly suggest where I had gone wrong.
thanks & regards
pjp
May 10, 2006 - 9:27 am UTC
why, they are both the last day of the month.
Months are not of fixed size. It makes no sense to say "3 days" in this particular case - last day of the month is a special case by our convention (our being "people").
compare only time component
karma, January 30, 2007 - 11:17 am UTC
I have one column defined timestamp in table A and date as another columns in table B. What is the best way to join this table efficiently to compare only time component?
Also how should we index on both tables
January 31, 2007 - 10:37 am UTC
join does NOT imply index. If you are joining every row in a table to every row in another table - you would want two full scans and a hash join typically (unless initial response time was your goal)
you would use a function to extract just the time component and compare using equality.
Weird DateDiff Error
An Avid Reader, May 23, 2007 - 3:52 pm UTC
I'm having problems using the function supplied in this thread within a PL/SQL package and it seems to be isolated when the end time is set to midnight. It works great from the SQL prompt and when called directly from the package:
SQL> SELECT (to_date('05-MAR-07 12.00.00 AM', 'DD-MON-YY HH.MI.SS AM')
2 - to_date('04-MAR-07 06.00.00 PM', 'DD-MON-YY HH.MI.SS AM'))
3 * DECODE( UPPER('mi'), 'SS', 24*60*60, 'MI', 24*60, 'HH', 24, NULL ) diff
4 FROM DUAL;
DIFF
----------
360
SQL> select pkg_metrics.datediff('MI',
2 to_date('04-MAR-07 06.00.00 PM', 'DD-MON-YY HH.MI.SS AM'),
3 to_date('05-MAR-07 12.00.00 AM', 'DD-MON-YY HH.MI.SS AM') ) diff from dual;
DIFF
----------
360
But when I used a stored procedure to call it I get a ORA-06502: PL/SQL: numeric or value error: number precision value too large error. That would normally indicate a problem with variable size but I'm pretty sure I've checked that:
..snip
l_diff NUMBER(9) := 0;
CURSOR date_cur(i_date in DATE) IS
select to_char(start_time, 'HH24') + 0 st,
decode(to_char(end_time, 'HH24') - 1, -1, 24, to_char(end_time, 'HH24') - 1) et,
start_time, end_time
from core_hours where app_id=i_app and core_date = i_date;
date_cur_rec date_cur%ROWTYPE;
CURSOR day_cur(i_date in DATE) IS
select to_char(start_time, 'HH24') + 0 st,
decode(to_char(end_time, 'HH24') - 1, -1, 24, to_char(end_time, 'HH24') - 1) et,
start_time, end_time
from core_hours where app_id=i_app and core_day = rtrim(to_char(i_date, 'Day'));
day_cur_rec day_cur%ROWTYPE;
BEGIN
select (to_char(last_day(l_date), 'dd') + 0) into l_num_days from dual;
WHILE l_day <= l_num_days
LOOP
OPEN date_cur(l_date);
FETCH date_cur INTO date_cur_rec;
IF date_cur%NOTFOUND then
CLOSE date_cur;
OPEN day_cur(l_date);
FETCH day_cur INTO day_cur_rec;
IF day_cur%NOTFOUND or day_cur_rec.st is null then
l_noncore := l_noncore + 1440;
ELSE
-- HERE'S THE PROBLEM LINE
l_diff := datediff('MI', day_cur_rec.start_time, day_cur_rec.end_time);
l_core := l_core + l_diff;
l_noncore := l_noncore + (1440 - l_diff);
END IF;
CLOSE day_cur;
ELSE
l_diff := datediff('MI', date_cur_rec.start_time, date_cur_rec.end_time);
l_core := l_core + l_diff;
l_noncore := l_noncore + (1440 - l_diff);
CLOSE date_cur;
END IF;
l_date := l_date + 1;
l_day := l_day + 1;
END LOOP;
..snip
The code works until the loop hits the dates from the 2 working examples above.
TIA
May 26, 2007 - 9:39 am UTC
give us a test case we can actually run in our database - no snip.... full code BUT SHORT CODE -get rid of anything not relevant to the problem.
Weird DateDiff Error pt 2
An Avid Reader, May 23, 2007 - 6:01 pm UTC
It occurred to me I didn't include the actual function:
FUNCTION datediff( i_what IN VARCHAR2, i_start IN DATE, i_end IN DATE ) RETURN NUMBER
as
l_result NUMBER(9);
BEGIN
SELECT (i_end-i_start) *
DECODE( UPPER(i_what),
'SS', 24*60*60, 'MI', 24*60, 'HH', 24, NULL )
INTO l_result FROM DUAL;
RETURN l_result;
EXCEPTION
WHEN OTHERS THEN
g_where := '-> ERROR from function datediff('|| SQLCODE || ',' || SUBSTR(SQLERRM,1,100) || ')';
RAISE;
END datediff;
May 26, 2007 - 9:40 am UTC
now, show us how to call this to produce your issue - small test case, complete with code to reproduce your problem.
A Weird DateDiff Error, cont.
An Avid Reader, May 25, 2007 - 1:22 pm UTC
Sorry to make a short story long but there must be an issue with the database in the way the dates are stored. The errors above only occur when I use the values stored in my table:
SQL> select to_char(start_time,'DD-MON-YY HH.MI.SS AM') strt,
2 to_char(end_time, 'DD-MON-YY HH.MI.SS AM') endt,
3 end_time - start_time
4 from core_hours where core_date = '04-mar-07' and app_id=69;
STRT ENDT END_TIME-START_TIME
--------------------- --------------------- -------------------
04-MAR-07 06.00.00 PM 05-MAR-07 12.00.00 AM -730486.75
Weird, huh? I have no idea why I get the huge negative number when subtracting the values but that explains the error anyway.
May 26, 2007 - 12:13 pm UTC
select to_char( start_time... )
dump(start_time)
and the same for end time for us please.
Helena Marková, May 28, 2007 - 4:13 am UTC
It is very interesting this dump() function.
re: An Avid Reader - Weird DateDiff error
yan, May 29, 2007 - 10:50 am UTC
I've encountered similar results when there was bad date data involved. The year portion was incorrectly converted from a 2 digit year to a 4 digit year.
Using the dump command as Tom suggested or selecting the 4 digit year will show if this is the problem.
SQL> l
1 select to_date('05-MAR-0007 12:00:00 AM','DD-MON-YYYY HH:MI:SS AM')-
2 to_date('04-MAR-2007 06:00:00 PM','DD-MON-YYYY HH:MI:SS AM') date_diff
3* from dual
SQL> /
DATE_DIFF
----------
-730486.75
re: An Avid Reader - Weird DateDiff error
An Avid Reader, June 04, 2007 - 3:05 pm UTC
Yes, thank you. The application was incorrectly formatting dates on the way in.
Thanks for the reference to the dump() function, Tom. That is a good tool for debugging date problems.
Occurances between a 12-month period
Ramis Shah, July 02, 2007 - 4:35 pm UTC
Hello Gentlemen
I am facing a problem hope you would solve it.
I have a table
CREATE TABLE MATCHES
(MT_ID NUMBER(4) CONSTRAINT MT_ID_PK PRIMARY KEY,
START_DATE DATE,
END_DATE DATE,
TEAM_A NUMBER(2),
TEAM_B NUMBER(2),
SERIES_ID NUMBER(3))
INSERT INTO MATCHES (MT_ID,START_DATE,END_DATE,TEAM_A,TEAM_B,SERIES_ID) VALUES(1,'1 Apr 1993', '5 Apr 1993',1,2,1);
INSERT INTO MATCHES (MT_ID,START_DATE,END_DATE,TEAM_A,TEAM_B,SERIES_ID) VALUES(2,'21 Apr 1993', '25 Apr 1993',2,1,1);
INSERT INTO MATCHES (MT_ID,START_DATE,END_DATE,TEAM_A,TEAM_B,SERIES_ID) VALUES(3,'30 Mar 1994', '3 Apr 1994',4,2,1);
INSERT INTO MATCHES (MT_ID,START_DATE,END_DATE,TEAM_A,TEAM_B,SERIES_ID) VALUES(4,'1 May 1994', '5 May 1994',8,1,1);
INSERT INTO MATCHES (MT_ID,START_DATE,END_DATE,TEAM_A,TEAM_B,SERIES_ID) VALUES(5,'24 Nov 1994', '28 Nov 1994',5,7,1);
INSERT INTO MATCHES (MT_ID,START_DATE,END_DATE,TEAM_A,TEAM_B,SERIES_ID) VALUES(6,'31 Mar 1999', '4 Apr 1999',3,6,1);
select * from matches order by 1
MT_ID START_DATE END_DATE TEAM_A TEAM_B SERIES_ID
1 01-APR-93 05-APR-93 1 2 1
2 21-APR-93 25-APR-93 2 1 1
3 30-MAR-94 03-APR-94 4 2 1
4 01-MAY-94 05-MAY-94 8 1 1
5 24-NOV-94 28-NOV-94 5 7 1
6 31-MAR-99 04-APR-99 3 6 1
Where
MT_ID is Match ID which is primary key
stat_date is the start date of the match and end_date is the date on which the match ended.
Each match has two teams i.e Team A and Team B. Each team has its unique ID. So in the above data there are eight teams form
ID 1 to 8.
Duration of match can be taken out if we minus the start date from the end date. For example:
Select END_DATE-START_DATE +1
From MATCHES
/
END_DATE-START_DATE+1
5
5
5
5
5
5
1 is added to include the start day too.
My requirment is as follows:
For each of the 8 Teams, show
1. How many matches they played in each of the 7 twelve month periods from 1 April 1993 to 31 March 2000? If a match overlaped a 12 month period, then it would be treated as one match each on either side on the 12 month period.
For example, if a match started on 30 March 1994 and ended on 3 Apr 1994 then for the 12-month year starting from 1 Apr 1993 to 31 March 1994 it would be treated as one match and for the 12-month period starting from 1 Apr 1994 to 31 March 1995 it would be treated as another match. Becuase the match was split into 2 + 3 days on either side of the 12-month period.
2. How many days each team was playing a match in each of the 7 twelve month periods from 1 April 1993 to 31 March 2000?
For example, a team played 3 matches between Apr 1, 1993 and Mar 31, 1994. The Duration of each of the matches was 5+5+5 = 15 days. But the third match overlaped the 12-month period which started on 30 Mar 1994 and ended on 3 Apr 1994. So the correct number of playing days for that team would be 5 + 5 + 2 = 12 days from Apr 1, 1993 and Mar 31, 1994.
A want an efficient query that gets me both of the above answers
My desired output is like this:
For Team ID = 1
Period MT Playing Days
1-Apr-1993 to 31-Mar-1994 2 10
1-Apr-1994 to 31-Mar-1995 1 5
1-Apr-1995 to 31-Mar-1996 0 0
1-Apr-1996 to 31-Mar-1997 0 0
1-Apr-1997 to 31-Mar-1998 0 0
1-Apr-1998 to 31-Mar-1999 0 0
1-Apr-1999 to 31-Mar-2000 0 0
For Team ID = 2
Period MT Playing Days
1-Apr-1993 to 31-Mar-1994 3 12
1-Apr-1994 to 31-Mar-1995 1 3
1-Apr-1995 to 31-Mar-1996 0 0
1-Apr-1996 to 31-Mar-1997 0 0
1-Apr-1997 to 31-Mar-1998 0 0
1-Apr-1998 to 31-Mar-1999 0 0
1-Apr-1999 to 31-Mar-2000 0 0
For Team ID = 3
Period MT Playing Days
1-Apr-1993 to 31-Mar-1994 0 0
1-Apr-1994 to 31-Mar-1995 0 0
1-Apr-1995 to 31-Mar-1996 0 0
1-Apr-1996 to 31-Mar-1997 0 0
1-Apr-1997 to 31-Mar-1998 0 0
1-Apr-1998 to 31-Mar-1999 1 1
1-Apr-1999 to 31-Mar-2000 1 4
For Team ID = 4
Period MT Playing Days
1-Apr-1993 to 31-Mar-1994 1 2
1-Apr-1994 to 31-Mar-1995 1 3
1-Apr-1995 to 31-Mar-1996 0 0
1-Apr-1996 to 31-Mar-1997 0 0
1-Apr-1997 to 31-Mar-1998 0 0
1-Apr-1998 to 31-Mar-1999 0 0
1-Apr-1999 to 31-Mar-2000 0 0
For Team ID = 5
Period MT Playing Days
1-Apr-1993 to 31-Mar-1994 0 0
1-Apr-1994 to 31-Mar-1995 1 5
1-Apr-1995 to 31-Mar-1996 0 0
1-Apr-1996 to 31-Mar-1997 0 0
1-Apr-1997 to 31-Mar-1998 0 0
1-Apr-1998 to 31-Mar-1999 0 0
1-Apr-1999 to 31-Mar-2000 0 0
For Team ID = 6
Period MT Playing Days
1-Apr-1993 to 31-Mar-1994 0 0
1-Apr-1994 to 31-Mar-1995 0 0
1-Apr-1995 to 31-Mar-1996 0 0
1-Apr-1996 to 31-Mar-1997 0 0
1-Apr-1997 to 31-Mar-1998 0 0
1-Apr-1998 to 31-Mar-1999 1 1
1-Apr-1999 to 31-Mar-2000 1 4
For Team ID = 7
Period MT Playing Days
1-Apr-1993 to 31-Mar-1994 0 0
1-Apr-1994 to 31-Mar-1995 1 5
1-Apr-1995 to 31-Mar-1996 0 0
1-Apr-1996 to 31-Mar-1997 0 0
1-Apr-1997 to 31-Mar-1998 0 0
1-Apr-1998 to 31-Mar-1999 0 0
1-Apr-1999 to 31-Mar-2000 0 0
For Team ID = 8
Period MT Playing Days
1-Apr-1993 to 31-Mar-1994 0 0
1-Apr-1994 to 31-Mar-1995 1 5
1-Apr-1995 to 31-Mar-1996 0 0
1-Apr-1996 to 31-Mar-1997 0 0
1-Apr-1997 to 31-Mar-1998 0 0
1-Apr-1998 to 31-Mar-1999 0 0
1-Apr-1999 to 31-Mar-2000 0 0
I would appreciate a prompt reply in this regard.
Thanks in advance
Ramis Shah
July 03, 2007 - 10:26 am UTC
... I would appreciate a prompt reply in this regard. ....
:) that always makes me smile.
ops$tkyte%ORA10GR2> with data
2 as
3 (
4 select sdate, add_months(sdate,12)-1 edate
5 from
6 (
7 select add_months( to_date(:rpt_start,'dd-mon-yyyy'), (level-1)*12 ) sdate
8 from dual
9 connect by level <= 7
10 )
11 ),
12 two_rows
13 as
14 (select 1 r from dual union all select 2 r from dual)
15 select team, sdate, edate, count(*) mt, sum(days) days
16 from (
17 select data.sdate, data.edate,
18 matches.start_date, matches.end_date,
19 least( data.edate, matches.end_date )-greatest( data.sdate, matches.start_date )+1 days,
20 decode( two_rows.r, 1, team_a, team_b ) team
21 from data, two_rows, matches
22 where data.edate >= matches.start_date
23 and data.sdate <= matches.end_date
24 )
25 group by team, sdate, edate
26 order by team, sdate
27 /
TEAM SDATE EDATE MT DAYS
---------- --------- --------- ---------- ----------
1 01-APR-93 31-MAR-94 2 10
01-APR-94 31-MAR-95 1 5
2 01-APR-93 31-MAR-94 3 12
01-APR-94 31-MAR-95 1 3
3 01-APR-98 31-MAR-99 1 1
01-APR-99 31-MAR-00 1 4
4 01-APR-93 31-MAR-94 1 2
01-APR-94 31-MAR-95 1 3
5 01-APR-94 31-MAR-95 1 5
6 01-APR-98 31-MAR-99 1 1
01-APR-99 31-MAR-00 1 4
7 01-APR-94 31-MAR-95 1 5
8 01-APR-94 31-MAR-95 1 5
13 rows selected.
I'll leave it as an exercise for you if you truly think you need those zero lines in there - it can in fact be done...
Another solution
Michel Cadot, July 03, 2007 - 3:39 pm UTC
SQL> with
2 data as (
3 select decode(trunc(rn/2),0,team_a,team_b) team,
4 decode(mod(rn,2),
5 0,start_date,
6 decode(trunc(months_between(start_date,to_date('01/04/1993','DD/MM/YYYY'))/12),
7 trunc(months_between(end_date,to_date('01/04/1993','DD/MM/YYYY'))/12),
8 start_date,
9 add_months(to_date('01/04/1993','DD/MM/YYYY'),
10 12*trunc(months_between(end_date,to_date('01/04/1993','DD/MM/YYYY'))/12))
11 )
12 ) start_date,
13 decode(mod(rn,2),
14 1,end_date,
15 decode(trunc(months_between(start_date,to_date('01/04/1993','DD/MM/YYYY'))/12),
16 trunc(months_between(end_date,to_date('01/04/1993','DD/MM/YYYY'))/12),
17 end_date,
18 add_months(to_date('01/04/1993','DD/MM/YYYY'),
19 12*trunc(months_between(end_date,to_date('01/04/1993','DD/MM/YYYY'))/12))
20 - 1)
21 ) end_date,
22 decode(mod(rn,2),
23 0, trunc(months_between(start_date,to_date('01/04/1993','DD/MM/YYYY'))/12),
24 trunc(months_between(end_date,to_date('01/04/1993','DD/MM/YYYY'))/12)
25 ) period
26 from matches,
27 (select level-1 rn from dual connect by level <= 4)
28 where mod(rn,2) = 0
29 or trunc(months_between(start_date,to_date('01/04/1993','DD/MM/YYYY'))/12)
30 != trunc(months_between(end_date,to_date('01/04/1993','DD/MM/YYYY'))/12)
31 )
32 select team,
33 add_months(to_date('01/04/1993','DD/MM/YYYY'),12*period) start_period,
34 add_months(to_date('01/04/1993','DD/MM/YYYY'),12*(period+1))-1 end_period,
35 count(*) mt,
36 sum(end_date-start_date+1) days
37 from data
38 group by team, period
39 order by team, start_period
40 /
TEAM START_PERIO END_PERIOD MT DAYS
---------- ----------- ----------- ---------- ----------
1 01-APR-1993 31-MAR-1994 2 10
01-APR-1994 31-MAR-1995 1 5
2 01-APR-1993 31-MAR-1994 3 12
01-APR-1994 31-MAR-1995 1 3
3 01-APR-1998 31-MAR-1999 1 1
01-APR-1999 31-MAR-2000 1 4
4 01-APR-1993 31-MAR-1994 1 2
01-APR-1994 31-MAR-1995 1 3
5 01-APR-1994 31-MAR-1995 1 5
6 01-APR-1998 31-MAR-1999 1 1
01-APR-1999 31-MAR-2000 1 4
7 01-APR-1994 31-MAR-1995 1 5
8 01-APR-1994 31-MAR-1995 1 5
13 rows selected.
Regards
Michel
The requested URL /apex/wwv_flow.show was not found on this server
Ramis, July 10, 2007 - 1:09 pm UTC
Hi Tom
I am unable to run a query (below) it as it keeps on giving the following error.
Not found
The requested URL /apex/wwv_flow.show was not found on this server
link of the erroer print screen
http://www5.upload2.net/download/80d58c6c5cb8da74ed439f810680a69b/4693a03e/bWxPogIFBs8Xe11/Error.JPG I am using XP home edition. Please advise what's wrong with this:
My oracle db is:
BANNER
Oracle Database 10g Express Edition Release 10.2.0.1.0 - Product
PL/SQL Release 10.2.0.1.0 - Production
CORE 10.2.0.1.0 Production
TNS for 32-bit Windows: Version 10.2.0.1.0 - Production
NLSRTL Version 10.2.0.1.0 - Production
The query is at the end, here is DDL data
CREATE TABLE MATCHES
(MT_ID NUMBER(4) CONSTRAINT MT_ID_PK PRIMARY KEY,
START_DATE DATE,
END_DATE DATE,
TEAM_A NUMBER(2),
TEAM_B NUMBER(2))
INSERT INTO MATCHES (MT_ID,START_DATE,END_DATE,TEAM_A,TEAM_B) VALUES(1,'1 Apr 1993', '5 Apr 1993',1,2);
INSERT INTO MATCHES (MT_ID,START_DATE,END_DATE,TEAM_A,TEAM_B) VALUES(2,'21 Apr 1993', '25 Apr 1993',2,1);
INSERT INTO MATCHES (MT_ID,START_DATE,END_DATE,TEAM_A,TEAM_B) VALUES(3,'30 Mar 1994', '3 Apr 1994',4,2);
INSERT INTO MATCHES (MT_ID,START_DATE,END_DATE,TEAM_A,TEAM_B) VALUES(4,'1 May 1994', '5 May 1994',8,1);
INSERT INTO MATCHES (MT_ID,START_DATE,END_DATE,TEAM_A,TEAM_B) VALUES(5,'24 Nov 1994', '28 Nov 1994',5,7);
INSERT INTO MATCHES (MT_ID,START_DATE,END_DATE,TEAM_A,TEAM_B) VALUES(6,'31 Mar 1999', '4 Apr 1999',3,6);
INSERT INTO MATCHES (MT_ID,START_DATE,END_DATE,TEAM_A,TEAM_B) VALUES(7,'1 Apr 1999', '5 Apr 1999',7,8);
INSERT INTO MATCHES (MT_ID,START_DATE,END_DATE,TEAM_A,TEAM_B) VALUES(8,'29 Mar 2000', '2 Apr 2000',3,6);
INSERT INTO MATCHES (MT_ID,START_DATE,END_DATE,TEAM_A,TEAM_B) VALUES(9,'2 Jun 2000', '6 Jun 2000',4,5);
break on team nodup skip 1
with periods as ( select add_months(dt,12*(rownum-1)) start_period,
add_months(dt,12*rownum)-1 end_period
from (select to_date('1-Apr-1992') dt from dual)
connect by level <= 9),
matches_by_period as ( select start_period, end_period, start_date,
end_date,
case when start_date <= end_period and end_date
>= start_period
then least(end_date,end_period) -
greatest(start_date,start_period) + 1
end playing_days, team_a, team_b
from periods, matches)
select decode(grouping_id(team,period), 1, 'TOTAL' ) TOT, team, period,
sum(free_days) freedays,
max(exists_six_weeks_gap) exists_six_week_gap from (
select team
, to_char(start_period,'dd-Mon-yyyy') || ' to ' ||
to_char(end_period,'dd-Mon-yyyy') period
, case when max(end_period) >= max(min_end_dt_team) and
max(start_period) <= max(max_end_dt_team) then
max(end_dt) - min(start_dt) - sum(nvl(playing_days,0)) + 1
else 0 end free_days
, nvl(case when max(min_end_dt_team) between max(start_period) and
max(end_period) then
case when max(days_between_matches) >= 42
then 1 else 0 end
end,0) exists_six_weeks_gap
from (select nvl(team_a,team_b) team
, start_period
, end_period
, max(end_date) over (partition by nvl(team_a,team_b))
max_end_dt_team
, min(end_date) over (partition by nvl(team_a,team_b))
min_end_dt_team
, playing_days
, greatest(start_period,start_date) start_dt
, least(end_period, end_date) end_dt
, case when playing_days is not null then
nvl(start_date - lag(end_date) over
(partition by nvl(team_a,team_b),
start_period, end_period
order by nvl(team_a,team_b),
start_date, end_date), 42) end
days_between_matches
from (select start_period
, end_period
, start_date
, end_date
, team_a
, team_b
, sum(playing_days) playing_days
from matches_by_period
group by start_period, end_period, start_date, end_date,
grouping sets (team_a, team_b)
)
)
group by team, to_char(start_period,'dd-Mon-yyyy') || ' to ' ||
to_char(end_period,'dd-Mon-yyyy')
)
group by rollup(team, period)
having grouping_id(team,period) <= 1
months_between strange results
jj, August 27, 2007 - 11:58 am UTC
1 -
It is strange that moving only the month of the beggining date of a period, the fractional part (number of days of a 31-days months) changes.
(I need the period between the two dates included, so I subtract 1).
Example:
select
round((months_between(to_date('15/05/2007','dd-mm-yyyy'),to_date('01/01/2006','dd-mm-yyyy')-1)),10),
round((months_between(to_date('15/05/2007','dd-mm-yyyy'),to_date('01/02/2006','dd-mm-yyyy')-1)),10),
round((months_between(to_date('15/05/2007','dd-mm-yyyy'),to_date('01/03/2006','dd-mm-yyyy')-1)),10),
round((months_between(to_date('15/05/2007','dd-mm-yyyy'),to_date('01/04/2006','dd-mm-yyyy')-1)),10)
from dual;
The whole results:
end/date
start/date 15/05/2007
01/01/2006 16,48387097 ok 16 months and 15/31 days
01/02/2006 15,48387097
01/03/2006 14,58064516 <- ? 14 months and quite 18/31 days
01/04/2006 13,48387097
01/05/2006 12,51612903 <- ? 12 months and quite 16/31
01/06/2006 11,48387097
01/07/2006 10,51612903 <-
01/08/2006 9,483870968
01/09/2006 8,483870968
01/10/2006 7,516129032 <-
01/11/2006 6,483870968
01/12/2006 5,516129032 <-
01/01/2007 4,483870968
Others strange results:
select months_between(to_date('01/03/2006','dd-mm-yyyy'),to_date('28/02/2006','dd-mm-yyyy')) from dual;
0,1290322580645161290322580645161290322581
that in a 31-days month are 4 days instead of 1.
select 31 * months_between(to_date('01/05/2006','dd-mm-yyyy'),to_date('30/04/2006','dd-mm-yyyy')) from dual;
--> 2 dd
2- If I split the period, I have different results for the fractional part:
select
round((months_between(to_date('15/05/2007','dd-mm-yyyy'),to_date('01/03/2006','dd-mm-yyyy')-1)),10),
round((months_between(to_date('15/05/2007','dd-mm-yyyy'),to_date('01/01/2007','dd-mm-yyyy')-1) +months_between(to_date('31/12/2006','dd-mm-yyyy'),to_date('01/03/2006','dd-mm-yyyy')-1)
),10)from dual;
14,5806451613 14,4838709677
How it is possible to solve this problem?
September 04, 2007 - 12:10 pm UTC
months_between is documented as always working with a 31 day month by design.
http://docs.oracle.com/docs/cd/B19306_01/server.102/b14200/functions089.htm#i78039 ...
If date1 and date2 are either the same days of the month or both last days of months, then the result is always an integer.
Otherwise Oracle Database calculates the fractional portion of the result based on a 31-day month......
A month is a moving target - it is not like a "week" (7 days) or a day (24 hours), it is a nebulous thing.
Using simple math - you can get the months between as an integer, adjust the one date using that and subtract to get the number of months and number of days between - and then divide...
ops$tkyte%ORA10GR2> select dt1, dt2, round( months_between(dt1, dt2), 10 ) monthsbtwn,
2 trunc(months_between(dt1,dt2))+(dt1-add_months(dt2,trunc(months_between(dt1,dt2)) ))/31 otherway
3 from (select to_date( '15/05/2007', 'dd-mm-yyyy') dt1,
4 add_months( to_date( '01/01/2006', 'dd/mm/yyyy' ),level-1)-1 dt2 from dual connect by level <= 15 )
5 /
DT1 DT2 MONTHSBTWN OTHERWAY
--------- --------- ---------- ----------
15-MAY-07 31-DEC-05 16.483871 16.483871
15-MAY-07 31-JAN-06 15.483871 15.483871
15-MAY-07 28-FEB-06 14.5806452 14.483871
15-MAY-07 31-MAR-06 13.483871 13.483871
15-MAY-07 30-APR-06 12.516129 12.483871
15-MAY-07 31-MAY-06 11.483871 11.483871
15-MAY-07 30-JUN-06 10.516129 10.483871
15-MAY-07 31-JUL-06 9.48387097 9.48387097
15-MAY-07 31-AUG-06 8.48387097 8.48387097
15-MAY-07 30-SEP-06 7.51612903 7.48387097
15-MAY-07 31-OCT-06 6.48387097 6.48387097
15-MAY-07 30-NOV-06 5.51612903 5.48387097
15-MAY-07 31-DEC-06 4.48387097 4.48387097
15-MAY-07 31-JAN-07 3.48387097 3.48387097
15-MAY-07 28-FEB-07 2.58064516 2.48387097
15 rows selected.
Using between clause
Manju, September 26, 2007 - 6:27 pm UTC
Tom,
I am doing a select from the dba_audit_trail.
I want to retrieve data from the following columns for a specified range of dates based on the Timestamp.I am using ampersand(&) for the &fromtime and &totime. Here is my sql
select username,obj_name,action_name,to_char(timestamp,'DD-MON-YYYY HH24:MI:SS') TIMESTAMP from dba_audit_trail
where timestamp between to_char(timestamp,'DD-MON-YYYY HH24:MI:SS') &FROMTIME and
to_char(timestamp,'DD-MON-YYYY HH24:MI:SS') &TOTIME
/
Here is the result set
##########################
Enter value for fromtime: '30-OCT-2005 00:00:00'
Enter value for totime: '01-NOV-2005 00:00:00'
old 2: where timestamp between to_char(timestamp,'DD-MON-YYYY HH24:MI:SS') &FROMTIME and to_char(timestamp,'DD-MON-YYYY HH24:MI:SS') &TOTIME
new 2: where timestamp between to_char(timestamp,'DD-MON-YYYY HH24:MI:SS') '30-OCT-2005 00:00:00' and to_char(timestamp,'DD-MON-YYYY HH24:MI:SS') '01-NOV-2005 00:00:00'
where timestamp between to_char(timestamp,'DD-MON-YYYY HH24:MI:SS') '30-OCT-2005 00:00:00' and to_char(timestamp,'DD-MON-YYYY HH24:MI:SS') '01-NOV-2005 00:00:00'
*
ERROR at line 2:
ORA-00905: missing keyword
#############################
What am i missing here ?
Thanks,
Manju
September 26, 2007 - 10:15 pm UTC
seems that
where timestamp between to_char(timestamp,'DD-MON-YYYY HH24:MI:SS') &FROMTIME
and
to_char(timestamp,'DD-MON-YYYY HH24:MI:SS') &TOTIME
/
should be
where timestamp between to_char('&fromtime','DD-MON-YYYY HH24:MI:SS')
and
to_char('&totime','DD-MON-YYYY HH24:MI:SS')
/
Finding difference in minutes between two timestamp columns
Bob, March 06, 2008 - 12:42 pm UTC
Hi Tom,
Suppose I have a table defined like this:
SQL> desc T_JACOFACRELEASETABLE
PAYMENTREF NOT NULL NUMBER
CREATEDUSERREF NUMBER
DATERELEASED TIMESTAMP(6)
LASTUPDATEDUSERREF NUMBER
LASTUPDATED TIMESTAMP(6)
OFACRELEASED NUMBER
Now I want to find all paymentref's that have a datereleased
more than five minutes ago.
How can I do that using SQL with a timestamp column?
March 07, 2008 - 5:05 pm UTC
ops$tkyte%ORA10GR2> create table t ( x timestamp, y timestamp );
Table created.
ops$tkyte%ORA10GR2>
ops$tkyte%ORA10GR2> begin
2 for i in 1 .. 7
3 loop
4 insert into t(x) values ( systimestamp );
5 dbms_lock.sleep(1);
6 end loop;
7
8 update t set y = systimestamp;
9 end;
10 /
PL/SQL procedure successfully completed.
ops$tkyte%ORA10GR2>
ops$tkyte%ORA10GR2> select x, y, y-x
2 from t
3 /
X Y Y-X
---------------------------- ---------------------------- ----------------------------
07-MAR-08 05.07.28.825031 PM 07-MAR-08 05.07.35.867900 PM +000000000 00:00:07.042869
07-MAR-08 05.07.29.826685 PM 07-MAR-08 05.07.35.867900 PM +000000000 00:00:06.041215
07-MAR-08 05.07.30.826920 PM 07-MAR-08 05.07.35.867900 PM +000000000 00:00:05.040980
07-MAR-08 05.07.31.828508 PM 07-MAR-08 05.07.35.867900 PM +000000000 00:00:04.039392
07-MAR-08 05.07.32.829632 PM 07-MAR-08 05.07.35.867900 PM +000000000 00:00:03.038268
07-MAR-08 05.07.33.830995 PM 07-MAR-08 05.07.35.867900 PM +000000000 00:00:02.036905
07-MAR-08 05.07.34.831064 PM 07-MAR-08 05.07.35.867900 PM +000000000 00:00:01.036836
7 rows selected.
ops$tkyte%ORA10GR2> select x, y, y-x
2 from t
3 where y-x > numtodsinterval(5,'second')
4 /
X Y Y-X
---------------------------- ---------------------------- ----------------------------
07-MAR-08 05.07.28.825031 PM 07-MAR-08 05.07.35.867900 PM +000000000 00:00:07.042869
07-MAR-08 05.07.29.826685 PM 07-MAR-08 05.07.35.867900 PM +000000000 00:00:06.041215
07-MAR-08 05.07.30.826920 PM 07-MAR-08 05.07.35.867900 PM +000000000 00:00:05.040980
Comparison of two dates
Bob, March 07, 2008 - 5:24 pm UTC
Hi Tom - that's absolutely fantastic. I never knew about numtodsinterval and its usage!
Thank you so much!
Datediff
Sagar, June 10, 2008 - 1:25 pm UTC
I have written this datediff as the month is always moving sometimes 28 , 29 , 30 or 31.
CREATE OR REPLACE procedure datediff(big_date in date , small_date in date,years out number,months out number , days out number)
is
pl_big_date date;
pl_small_date date;
pl_years number;
pl_months number;
pl_days number;
begin
days:=0;
months:=0;
years:=0;
pl_big_date:=trunc(big_date);
pl_small_date := trunc(small_date);
while(pl_small_date <= pl_big_date)
loop
days:=days+1;
pl_small_date := pl_small_date + 1 ;
--dbms_output.put_line('days ' || days);
if(pl_small_Date > add_months(small_date,1+months + (12 * years) )) then
days:=0;
months:=months+1;
--dbms_output.put_line('months ' || months);
end if;
if(pl_small_Date > add_months(small_date,12+ (12 * years))) then
days:=0;
months:=0;
--dbms_output.put_line('years ' || years );
years:=years+1;
end if;
end loop;
end;
/
SQL> ed
Wrote file afiedt.buf
1 DECLARE
2 BIG_DATE DATE;
3 SMALL_DATE DATE;
4 pl_YEARS NUMBER;
5 pl_MONTHS NUMBER;
6 pl_dAYS NUMBER;
7 BEGIN
8 BIG_DATE := '10-jun-2008';
9 SMALL_DATE := '30-nov-1978';
10 SCOTT.DATEDIFF ( BIG_DATE, SMALL_DATE, pl_YEARS, pl_MONTHS, pl_DAYS );
11 dbms_output.put_line(pl_years);
12 dbms_output.put_line(pl_months);
13 dbms_output.put_line(pl_days);
14 COMMIT;
15* END;
SQL> /
29
6
10
PL/SQL procedure successfully completed.
June 10, 2008 - 2:52 pm UTC
explain to me the logic:
14 COMMIT;
why why why would you do that -EVER.
ugh. that is the worst - the pits. I hope you don't throw spurious commits in your code like that in real life.
I've no idea what your "datediff" is or does or why it is better. We are humans, not compilers. If you don't explain it - it doesn't count.
So what if there are different days in the month? the builtins sort of *deal* with that. You seem to have written a lot of procedural code to do the same thing as a couple of builtin functions
Sagar, June 11, 2008 - 11:50 am UTC
Argh!...that commit...not required at all...(forgot to comment that, some famous code generator puts the commit there).
The logic is just trying to put in a code as how would we manually calculate difference in two dates for e.g. age by counting days , months and years.
June 11, 2008 - 4:00 pm UTC
but - the builtin functions do that, can you tell us why doing it with lots of code is "better"?
Sagar, June 12, 2008 - 12:59 am UTC
Just different way of doing it thats all.I have used builtins in my code as much as possible in my logic.
June 12, 2008 - 7:35 am UTC
compare:
ops$tkyte@8i> select months_between( end_date, start_date ),
2 trunc( months_between( end_date, start_date ) /12 ) Years,
3 mod( trunc( months_between( end_date, start_date ) ), 12 )
months,
4 end_date - add_months(start_date,trunc( months_between(
end_date, start_date ) )) days
5 from t
6 /
to
CREATE OR REPLACE procedure datediff(big_date in date , small_date in date,years out number,months
out number , days out number)
is
pl_big_date date;
pl_small_date date;
pl_years number;
pl_months number;
pl_days number;
begin
days:=0;
months:=0;
years:=0;
pl_big_date:=trunc(big_date);
pl_small_date := trunc(small_date);
while(pl_small_date <= pl_big_date)
loop
days:=days+1;
pl_small_date := pl_small_date + 1 ;
--dbms_output.put_line('days ' || days);
if(pl_small_Date > add_months(small_date,1+months + (12 * years) )) then
days:=0;
months:=months+1;
--dbms_output.put_line('months ' || months);
end if;
if(pl_small_Date > add_months(small_date,12+ (12 * years))) then
days:=0;
months:=0;
--dbms_output.put_line('years ' || years );
years:=years+1;
end if;
end loop;
end;
/
you need no procedural logic to
a) design
b) code
c) test
d) maintain
the builtin functions *do it all*, there is absolutely NO NEED to write any procedural code. Your approach should be actively discouraged, it is not the right approach at all. It is hugely wasteful of resources (cpu), not very understandable.
given that your 35 lines of code could be:
ops$tkyte%ORA10GR2> CREATE OR REPLACE procedure datediff(end_date in date , start_date in date,years out number,months out number , days out number)
2 is
3 begin
4 years := trunc( months_between( end_date, start_date ) /12 );
5 months := mod( trunc( months_between( end_date, start_date ) ), 12 );
6 days := end_date - add_months(start_date,trunc( months_between( end_date, start_date ) ));
7 end;
8 /
why would you do it that way?
Yeah got it thnx!!!...
Sagar, June 12, 2008 - 8:07 am UTC
I agree on that...no doubts...never claimed it to be better...we try to do things good way and still we find ways to do it better way.
Calc Number Of Hours
Ahmad Darwish, November 16, 2008 - 9:45 am UTC
Dear Tom.
I'Have Two Fields format is Date
Fist one labe is Stat_Date := 10-jan-2008 10:20:12 am
Second one labe is End_Date := 11-jan-2008 01:15:20 pm
now
how can get the period between to date in (Hours:Minute)
Thanks allot.
November 18, 2008 - 7:18 pm UTC
ops$tkyte%ORA10GR2> select sdate, edate,
2 trunc( (edate-sdate) * 24 ) hours,
3 ((edate-sdate)*24 - trunc( (edate-sdate) * 24 ))*60 minutes
4 from (
5 select to_date( '10-jan-2008 10:20:12 am', 'dd-mon-yyyy hh:mi:ss am' ) sdate,
6 to_date( '11-jan-2008 01:15:20 pm', 'dd-mon-yyyy hh:mi:ss am' ) edate
7 from dual
8 )
9 /
SDATE EDATE HOURS MINUTES
--------- --------- ---------- ----------
10-JAN-08 11-JAN-08 26 55.1333333
A reader, January 28, 2009 - 9:23 am UTC
Tom,
select systimestamp col,
(systimestamp-5/1440) minus_5,
(systimestamp+5/1440) plu_5
from dual;
I would like to get (systimestamp - 5 minutes) and
(systimestamp + 5 minutes) and display in timestamp format.
When I subtract 5 mins or add 5 minutes, it is displayed as MM/DD/YYYY HH:MI:SS AM. How to get it in timestamp format?
COL,MINUS_5,PLU_5
1/28/2009 9:09:10.993631 AM -05:00,1/28/2009 9:04:10 AM,1/28/2009 9:14:10 AM
January 28, 2009 - 3:55 pm UTC
ops$tkyte%ORA11GR1> select systimestamp, systimestamp+numtodsinterval( 5, 'minute' ), systimestamp-numtodsinterval( 5, 'minute' ) from dual;
SYSTIMESTAMP
---------------------------------------------------------------------------
SYSTIMESTAMP+NUMTODSINTERVAL(5,'MINUTE')
---------------------------------------------------------------------------
SYSTIMESTAMP-NUMTODSINTERVAL(5,'MINUTE')
---------------------------------------------------------------------------
28-JAN-09 03.53.48.092440 PM -05:00
28-JAN-09 03.58.48.092440000 PM -05:00
28-JAN-09 03.48.48.092440000 PM -05:00
use intervals...
date arithmetic works with DATES.. Intervals can be added to timestamps
date difference
A reader, February 11, 2009 - 5:09 am UTC
Hi Tom, i was looking for the time difference between two dates based on certain condition. I need to calculate the total troubleshoot duration(closure-troubleshoot) for a given ticket.For the below example it should give me 3 hrs but not 5 hrs. Can you please help.
Ticket status changedate
I1086 TRBLESHOOT 2/8/2008 9:40 am
I1086 CLOSURE 2/8/2008 10:40 am
I1086 TRBLESHOOT 2/8/2008 11.40 am
I1086 CLOSURE 2/8/2008 12:40 pm
I1086 TRBLESHOOT 2/8/2008 1:40 pm
I1086 CLOSURE 2/8/2008 2:40 pm
February 11, 2009 - 10:39 am UTC
no create table
no inserts
no look
and make it "interesting", one would presume there is more than one trouble ticket in the database
and make it more clear, phrase requirements in form of, well, requirements. for example I think you mean:
a) find the min change date for a ticket where status = trbleshoot
b) find the max change date for a ticket where status = closure
c) expected result is (b)-(c), for each ticket
(and you know what, if you phrase it that way, it almost answers itself - no?)
A reader, February 12, 2009 - 12:28 am UTC
Tom, thanks a lot for your response..:)
Apologies for not very clear of the requirement.Actually I require the data from the History table where we store the different statuses that a ticket undergoes. For Example:
Table Attribute
Ticket Ticket,status,changedate,TicketUid(unique)
Tktstatus(History)Ticket,status,changedate,Tktstatusid(unique)
where Ticket.Ticket equals Tktstatus.Ticket
Any change in STATUS of the Ticket table will be captured in History table Tktstatus.
Below is the data from Tktstatus - History table.
Ticket status changedate Tktstatusid
I1086 TRBLESHOOT 2/8/2008 9:40 am 1
I1086 CLOSURE 2/8/2008 10:40 am 2
I1086 TRBLESHOOT 2/8/2008 11.40 am 3
I1086 CLOSURE 2/8/2008 12:40 pm 4
I1086 TRBLESHOOT 2/8/2008 1:40 pm 5
I1086 CLOSURE 2/8/2008 2:40 pm 6
Max(closure) - Min(Trbleshoot) gives me 5 Hrs which is wrong.
I require the exact duration the ticket was in Troubleshoot duration.
ie., 1st ( CLOSURE minus TRBLESHOOT) + 2nd + 3rd
(2/8/2008 9:40 ¿ 2/8/2008 10.40) + (2/8/2008 11:40 ¿ 2/8/2008 12.40)+ (2/8/2008 1:40 ¿ 2/8/2008 2.40)
which is 1 + 1 + 1 = 3 Hrs
I have taken the duration in seconds.But this query gives me cartesian product. How to avoid those...please help.
select a.ticket,
trunc((b.changedate-a.changedate)*24*60*60) as Duration
from tktstatus a, tktstatus b
where a.ticket = b.ticket
b.changedate > a.changedate
and a. status = 'TRBLESHOOT'
and b.status = 'CLOSURE'
Thanks much.
February 12, 2009 - 12:13 pm UTC
no create table
no inserts
no look
A reader, February 13, 2009 - 3:58 am UTC
Tom,
Please use the below scripts.
CREATE TABLE TKTSTATUS
(
TICKETID VARCHAR2(10) NOT NULL,
STATUS VARCHAR2(10) NOT NULL,
CHANGEBY VARCHAR2(30) NOT NULL,
CHANGEDATE DATE NOT NULL,
TKSTATUSID NUMBER NOT NULL
);
Insert into TKTSTATUS
(TICKETID, STATUS, CHANGEBY, CHANGEDATE, TKSTATUSID)
Values
('I1086', 'TRBLESHOOT', 'MAXADMIN', TO_DATE('2/8/2008 9:40:40', 'MM/DD/YYYY HH24:MI:SS'), 1);
Insert into TKTSTATUS
(TICKETID, STATUS, CHANGEBY, CHANGEDATE, TKSTATUSID)
Values
('I1086', 'CLOSURE', 'MAXADMIN', TO_DATE('2/8/2008 10:40:40', 'MM/DD/YYYY HH24:MI:SS'), 2);
Insert into TKTSTATUS
(TICKETID, STATUS, CHANGEBY, CHANGEDATE, TKSTATUSID)
Values
('I1086', 'TRBLESHOOT', 'MAXADMIN', TO_DATE('2/8/2008 11:40:40', 'MM/DD/YYYY HH24:MI:SS'), 3);
Insert into TKTSTATUS
(TICKETID, STATUS, CHANGEBY, CHANGEDATE, TKSTATUSID)
Values
('I1086', 'CLOSURE', 'MAXADMIN', TO_DATE('2/8/2008 12:40:40', 'MM/DD/YYYY HH24:MI:SS'), 4);
Insert into TKTSTATUS
(TICKETID, STATUS, CHANGEBY, CHANGEDATE, TKSTATUSID)
Values
('I1086', 'TRBLESHOOT', 'MAXADMIN', TO_DATE('2/8/2008 13:40:40', 'MM/DD/YYYY HH24:MI:SS'), 5);
Insert into TKTSTATUS
(TICKETID, STATUS, CHANGEBY, CHANGEDATE, TKSTATUSID)
Values
('I1086', 'CLOSURE', 'MAXADMIN', TO_DATE('2/8/2008 14:40:40', 'MM/DD/YYYY HH24:MI:SS'), 6);
Required: The exact duration the ticket was in Troubleshoot duration.
ie., 1st ( CLOSURE minus TRBLESHOOT) + 2nd + 3rd
(2/8/2008 9:40:40 - 2/8/2008 10:40:40) + (2/8/2008 11:40:40 - 2/8/2008 12:40:40)+ (2/8/2008 13:40:40 - 2/8/2008 14:40:40)
Hope this helps.
Thanks for your patience.
February 16, 2009 - 10:31 am UTC
guess you missed this entirely:
... and make it "interesting", one would presume there is more than one trouble ticket in the database ....
oh well, hard to test things without boundary conditions and interesting data.
<b>
marry up each record with the 'next' record by changedate. Note: this will all assume that TRBLESHOOT, CLOSURE are the only things in this table, if not - you know what to do - ADD A PREDICATE to only get trbleshoot, closure records....
and you must decide what to do if you have a trbleshoot record that is NOT followed by a closure record - you will need to default next_changedate to a value that pleases you (maybe sysdate? I don't know, insufficient data to tell you want to do there....)
</b>
ops$tkyte%ORA10GR2> select ticketid,
2 status, changedate,
3 lead(status) over (partition by ticketid order by changedate) next_status,
4 lead(changedate) over (partition by ticketid order by changedate) next_changedate
5 from tktstatus
6 /
TICKETID STATUS CHANGEDATE NEXT_STATU NEXT_CHANGEDATE
---------- ---------- -------------------- ---------- --------------------
I1086 TRBLESHOOT 08-feb-2008 09:40:40 CLOSURE 08-feb-2008 10:40:40
I1086 CLOSURE 08-feb-2008 10:40:40 TRBLESHOOT 08-feb-2008 11:40:40
I1086 TRBLESHOOT 08-feb-2008 11:40:40 CLOSURE 08-feb-2008 12:40:40
I1086 CLOSURE 08-feb-2008 12:40:40 TRBLESHOOT 08-feb-2008 13:40:40
I1086 TRBLESHOOT 08-feb-2008 13:40:40 CLOSURE 08-feb-2008 14:40:40
I1086 CLOSURE 08-feb-2008 14:40:40
6 rows selected.
<b>then just keep the interesting records...</b>
ops$tkyte%ORA10GR2>
ops$tkyte%ORA10GR2> select ticketid, status, next_status, next_changedate,changedate
2 from (
3 select ticketid,
4 status, changedate,
5 lead(status) over (partition by ticketid order by changedate) next_status,
6 lead(changedate) over (partition by ticketid order by changedate) next_changedate
7 from tktstatus
8 )
9 where status = 'TRBLESHOOT'
10 /
TICKETID STATUS NEXT_STATU NEXT_CHANGEDATE CHANGEDATE
---------- ---------- ---------- -------------------- --------------------
I1086 TRBLESHOOT CLOSURE 08-feb-2008 10:40:40 08-feb-2008 09:40:40
I1086 TRBLESHOOT CLOSURE 08-feb-2008 12:40:40 08-feb-2008 11:40:40
I1086 TRBLESHOOT CLOSURE 08-feb-2008 14:40:40 08-feb-2008 13:40:40
<b>and aggregate</b>
ops$tkyte%ORA10GR2>
ops$tkyte%ORA10GR2> select ticketid, sum(next_changedate-changedate)*24*60*60 seconds
2 from (
3 select ticketid,
4 status, changedate,
5 lead(status) over (partition by ticketid order by changedate) next_status,
6 lead(changedate) over (partition by ticketid order by changedate) next_changedate
7 from tktstatus
8 )
9 where status = 'TRBLESHOOT'
10 group by ticketid
11 /
TICKETID SECONDS
---------- ----------
I1086 10800
Excellent Sir
Gita, February 17, 2009 - 8:44 am UTC
Awesome..!!! Thanks a lot Tom.. :) the way you narrate and solve the queries indeed helps beginners like me a lot to understand the database at ease :)
For your comment,
"... and make it "interesting", one would presume there is more than one trouble ticket in the database ...."
.... actually the data is from Ticket History table where we would capture each status change that a particular ticket undergoes..Hence there is more than one ticket of the same id.
You are right...as you guessed, there exists other statuses also for the ticket along with trbleshoot and closure.
FOr example
Ticket status changedate Tktstatusid
I1086 CREATION 2/8/2008 9:40 am 1
I1086 ACCEPTANCE 2/8/2008 10:40 am 2
I1086 TRBLESHOOT 2/8/2008 11.40 am 3
I1086 CLOSURE 2/8/2008 12:40 pm 4
I1086 TRBLESHOOT 2/8/2008 1:40 pm 5
I1086 CLOSURE 2/8/2008 2:40 pm 6
I1086 CLOSED 2/8/2008 3:40 pm 7
You have told me to use predicate function to get the trbleshoot and closure records...can u pls show me how to use that function for the above scenario..
Now i need to calculate the total Troubleshoot duration and need to display the result in DD HH:MM:SS format(day hours:minute:seconds).
Please use the below Scripts
CREATE TABLE TKTSTATUS
(
TICKETID VARCHAR2(10) NOT NULL,
STATUS VARCHAR2(10) NOT NULL,
CHANGEBY VARCHAR2(30) NOT NULL,
CHANGEDATE DATE NOT NULL,
TKSTATUSID NUMBER NOT NULL
);
Insert into TKTSTATUS
(TICKETID, STATUS, CHANGEBY, CHANGEDATE, TKSTATUSID)
Values
('I1086', 'CREATION', 'MAXADMIN', TO_DATE('2/8/2008 9:40:40', 'MM/DD/YYYY HH24:MI:SS'), 1);
Insert into TKTSTATUS
(TICKETID, STATUS, CHANGEBY, CHANGEDATE, TKSTATUSID)
Values
('I1086', 'ACCEPTANCE', 'MAXADMIN', TO_DATE('2/8/2008 10:40:40', 'MM/DD/YYYY HH24:MI:SS'), 2);
Insert into TKTSTATUS
(TICKETID, STATUS, CHANGEBY, CHANGEDATE, TKSTATUSID)
Values
('I1086', 'TRBLESHOOT', 'MAXADMIN', TO_DATE('2/8/2008 11:40:40', 'MM/DD/YYYY HH24:MI:SS'), 3);
Insert into TKTSTATUS
(TICKETID, STATUS, CHANGEBY, CHANGEDATE, TKSTATUSID)
Values
('I1086', 'CLOSURE', 'MAXADMIN', TO_DATE('2/8/2008 12:40:40', 'MM/DD/YYYY HH24:MI:SS'), 4);
Insert into TKTSTATUS
(TICKETID, STATUS, CHANGEBY, CHANGEDATE, TKSTATUSID)
Values
('I1086', 'TRBLESHOOT', 'ADMIN', TO_DATE('2/8/2008 13:40:40', 'MM/DD/YYYY HH24:MI:SS'), 5);
Insert into TKTSTATUS
(TICKETID, STATUS, CHANGEBY, CHANGEDATE, TKSTATUSID)
Values
('I1086', 'CLOSURE', 'ADMIN', TO_DATE('2/8/2008 14:40:40', 'MM/DD/YYYY HH24:MI:SS'), 6);
Insert into TKTSTATUS
(TICKETID, STATUS, CHANGEBY, CHANGEDATE, TKSTATUSID)
Values
('I1086', 'CLOSED', 'MAXADMIN', TO_DATE('2/8/2008 14:40:40', 'MM/DD/YYYY HH24:MI:SS'), 7);
Thanks in advance.
February 17, 2009 - 8:58 am UTC
you do not know how to add a where clause?
where status in ( 'a', 'b')
where a and b are the open/close events - just add that to the innermost query.
Thanks
A reader, February 18, 2009 - 10:04 pm UTC
Thanks Tom
date diff
Harschil Kaparwan, March 31, 2009 - 8:19 am UTC
Hi Tom,
Many thanks for your help.
I have :
ZZ@MYDB9I:XXXX> desc t1;
Name Null? Type
------------------------- -------- -----------------------------
OBJECT_NAME VARCHAR2(128)
CREATED DATE
LAST_DDL_TIME DATE
DAY_LIGHT_SAVING_IN_SEC NUMBER
ZZ@MYDB9I:XXXX>
with data :
INSERT INTO T1 ( OBJECT_NAME, CREATED, LAST_DDL_TIME,
DAY_LIGHT_SAVING_IN_SEC ) VALUES (
'RUNSTATS_PKG', TO_Date( '03/28/2009 12:00:00 AM', 'MM/DD/YYYY HH:MI:SS AM'), TO_Date( '03/29/2009 03:59:59 AM', 'MM/DD/YYYY HH:MI:SS AM')
, 3600);
INSERT INTO T1 ( OBJECT_NAME, CREATED, LAST_DDL_TIME,
DAY_LIGHT_SAVING_IN_SEC ) VALUES (
'C_OBJ#', TO_Date( '03/28/2008 12:00:00 AM', 'MM/DD/YYYY HH:MI:SS AM'), TO_Date( '03/29/2008 12:30:30 PM', 'MM/DD/YYYY HH:MI:SS AM')
, 3600);
COMMIT;
ZZ@MYDB9I:XXXX> select * from t1;
OBJECT_NAME CREATED LAST_DDL_TIME DAY_LIGHT_SAVING_IN_SEC
------------ ------------------- ------------------- --------------
RUNSTATS_PKG 28/03/2009 00:00:00 29/03/2009 03:59:59 3600
C_OBJ# 28/03/2008 00:00:00 29/03/2008 12:30:30 3600
ZZ@MYDB9I:XXXX>
Note: DAY_LIGHT_SAVING_IN_SEC , a number datatype stores the daylight saving in seconds
to calculate the difference in hours for
(last_ddl_time)-(created) , including DAY_LIGHT_SAVING_IN_SEC subtraction
i did
ZZ@MYDB9I:XXXX> select object_name,created,last_ddl_time,day_light_saving_in_sec, ((last_ddl_time-created)*24*60*60-DAY_LIGHT_SAVING_
IN_SEC)/60/60 DDL_MISSED_HRS from t1;
OBJECT_NAME CREATED LAST_DDL_TIME DAY_LIGHT_SAVING_IN_SEC DDL_MISSED_HRS
------------------ ------------------- --------------- ---------------- --------------
RUNSTATS_PKG 28/03/2009 00:00:00 29/03/2009 03:59:59 3600 26.9997222
C_OBJ# 28/03/2008 00:00:00 29/03/2008 12:30:30 3600 35.5083333
ZZ@MYDB9I:XXXX>
i wanted to display the output ((last_ddl_time-created)*24*60*60-DAY_LIGHT_SAVING_IN_SEC , which is in seconds as :
<m> days, <n> hrs, <o> mins, <p> secs format
Kindly help.
Regards
Harschil Kaparwan
March 31, 2009 - 9:09 am UTC
interesting
did you even read the original answer? Follow any of the links?
date diff .......
Harschil Kaparwan, March 31, 2009 - 9:33 am UTC
Yes Tom,
I read the original answer posted by you.
But could not make it for days, hrs, mins & secs.
also i have slightly different requirement here..
i am subtracting the column DAY_LIGHT_SAVING_IN_SEC, which is in seconds, also from the result set of two dates (last_ddl_time)-(created) and want to display the entire result in
<m> days, <n> hrs, <o> mins, <p> secs format
Many thanks for your time.
Regards
Harschil Kaparwan
April 01, 2009 - 7:01 am UTC
and the link? in the original answer? to a query that shows precisely how to do this?
You have a difference in dates A and B
(a-b) - seconds *1/24/60
follow the LINK as suggested and discover a query that shows precisely how to do this. In that query, when you follow that link, where I use
(sysdate-created)
you'll use
((a-b)-seconds*1/24/60)
instead
date diff .......
Harschil, April 01, 2009 - 8:20 am UTC
Hi Tom,
my fault, i didnt scanned the link properly.
Extremally sorry for making you to re-visit my question.
Kind Regards
Harschil
Months Between
Sachin, May 13, 2009 - 2:55 pm UTC
============================================================
Followup September 1, 2004 - 8am US/Eastern:
ops$tkyte@ORA9IR2> select d1, d2, add_months(trunc(d1,'mm'),r)
2 from t1, (select rownum-1 r
3 from all_objects
4 where rownum <= ( select max(months_between(d2,d1))+1 from t1) )
5 where months_between(d2,d1)+1 >= r
6 /
============================================================
I was expecting to see 3 rows for date d1='25-Mar-2009' and date d2='07-May-2009', but this SQL gives only two rows,one for March and one for April. Am I missing something here?
Thanks as always for your insightful replies.
Regards
Sachin
May 13, 2009 - 3:38 pm UTC
ops$tkyte%ORA10GR2> select d1, d2, add_months(trunc(d1,'mm'),r)
2 from t1, (select rownum-1 r
3 from all_objects
4 where rownum <= ( select max(months_between(trunc(d2,'mm'),trunc(d1,'mm')))+1 from t1) )
5 where months_between(d2,d1)+1 >= r
6 /
D1 D2 ADD_MONTH
--------- --------- ---------
25-MAR-09 07-MAY-09 01-MAR-09
25-MAR-09 07-MAY-09 01-APR-09
25-MAR-09 07-MAY-09 01-MAY-09
indeed, should have backed the start/end times off to the beginning of the month to make the math work properly when the start date is near the end of the month and the end date was near the beginning
Months Between
Sachin, May 13, 2009 - 6:45 pm UTC
Wonderful!
Thanks Tom!
A reader, December 14, 2009 - 5:51 am UTC
CREATE TABLE SCOTT.TEST_DATA
(
FROM_DATE DATE,
TO_DATE DATE,
ID NUMBER(2)
);
SET DEFINE OFF;
Insert into TEST_DATA
(FROM_DATE, TO_DATE, ID)
Values
(TO_DATE('12/01/2009 00:00:00', 'MM/DD/YYYY HH24:MI:SS'), TO_DATE('12/31/2009 00:00:00', 'MM/DD/YYYY HH24:MI:SS'), 1);
COMMIT;
if required all Tuesday then as follows
Tuesday
12/01/2009
12/08/2009
12/15/2009
If required all saturday and monday then follows
Saturday, Monday
12/05/2009
12/07/2009
12/12/2009
12/14/2009
December 14, 2009 - 9:53 am UTC
lovely.
Too bad we have no clue what you are trying to convey. Whether it be a question or whatever - there is no clue as to what you are saying...
A reader, December 15, 2009 - 1:15 am UTC
Sorry Tom
Want to get all the Saturday and Monday between dates (12/01/2009 and 12/15/2009 )
12/05/2009
12/07/2009
12/12/2009
12/14/2009
December 15, 2009 - 9:51 am UTC
Insert into TEST_DATA
(FROM_DATE, TO_DATE, ID)
Values
(TO_DATE('12/01/2009 00:00:00', 'MM/DD/YYYY HH24:MI:SS'), TO_DATE('12/31/2009 00:00:00',
'MM/DD/YYYY HH24:MI:SS'), 1);
I'm gobsmacked. How do you get from start/end of 1-dec, 31-dec to "just to the 15th please"
Seriously.
ops$tkyte%ORA9IR2> variable strt varchar2(30)
ops$tkyte%ORA9IR2> variable end varchar2(30)
ops$tkyte%ORA9IR2>
ops$tkyte%ORA9IR2> exec :strt := '1-dec-2009'; :end := '15-dec-2009';
PL/SQL procedure successfully completed.
ops$tkyte%ORA9IR2>
ops$tkyte%ORA9IR2> alter session set nls_date_format = 'dd-mon-yyyy Day';
Session altered.
ops$tkyte%ORA9IR2>
ops$tkyte%ORA9IR2> with data
2 as
3 (select level-1 l
4 from dual
5 connect by level <=
6 to_date( :end, 'dd-mon-yyyy' ) - to_date( :strt, 'dd-mon-yyyy' )+1 )
7 select to_date( :strt, 'dd-mon-yyyy' )+l
8 from data
9 where to_char( to_date(:strt,'dd-mon-yyyy')+l, 'dy' ) in ('sat','mon')
10 /
TO_DATE(:STRT,'DD-MON
---------------------
05-dec-2009 Saturday
07-dec-2009 Monday
12-dec-2009 Saturday
14-dec-2009 Monday
ops$tkyte%ORA9IR2> with data
2 as
3 (select level-1 l
4 from dual
5 connect by level <=
6 to_date( :end, 'dd-mon-yyyy' ) - to_date( :strt, 'dd-mon-yyyy' )+1 )
7 select to_date( :strt, 'dd-mon-yyyy' )+l
8 from data
9 where to_char( to_date(:strt,'dd-mon-yyyy')+l, 'd' )
10 in ( to_char( to_date( '15-03-1965', 'dd-mm-yyyy' ), 'd' ),
11 to_char( to_date( '13-03-1965', 'dd-mm-yyyy' ), 'd' ) )
12 /
TO_DATE(:STRT,'DD-MON
---------------------
05-dec-2009 Saturday
07-dec-2009 Monday
12-dec-2009 Saturday
14-dec-2009 Monday
the second one is more international as it doesn't rely on English.
A reader, December 16, 2009 - 7:51 am UTC
can you please explain use of "with data as"
December 16, 2009 - 10:27 am UTC
wrong century for the dates from flat file
A reader, January 06, 2010 - 8:43 pm UTC
I am in the process of uploading the data from flat file ( aka CSV) into oracle via external table .
All the dates have the year of 20xx. My understanding was 51-99 will be prefixed with 19 ( such as 1951 - 1999) and 00-50
will be prefixed with 20 ( such as 2004 ... )
The column below ( startdate ) is of timestamp .
Is my understanding wrong ?
SQL> select startdate , to_date(startdate , 'dd/mm/yyyy' ) newdate from tab;
NEW_DATE STARTDATE
---------- -------------------------
09/18/2090 18-SEP-90 12.00.00.000000000 AM
BANNER
--------------------------------------------------------------------------------
Oracle Database 11g Enterprise Edition Release 11.1.0.7.0 - 64bit Production
PL/SQL Release 11.1.0.7.0 - Production
CORE 11.1.0.7.0 Production
TNS for 64-bit Windows: Version 11.1.0.7.0 - Production
NLSRTL Version 11.1.0.7.0 - Production
SQL> show parameter nls
NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
nls_calendar string
nls_comp string BINARY
nls_currency string
nls_date_format string
nls_date_language string
nls_dual_currency string
nls_iso_currency string
nls_language string AMERICAN
nls_length_semantics string BYTE
nls_nchar_conv_excp string FALSE
nls_numeric_characters string
nls_sort string
nls_territory string AMERICA
nls_time_format string
nls_time_tz_format string
nls_timestamp_format string
nls_timestamp_tz_format string
SQL>
SQL>
January 11, 2010 - 8:09 am UTC
... Is my understanding wrong ? ...
yes.
If you give me a fully century and year - such as 2090 AS INPUT, and say "would you please convert that into century and year", what did you think would happen???
You have 2090 as input.
You already have *2090* as input.
2090 is a century plus year.
Also, yyyy says explicitly, "I'm giving you a century and year".
I think you want RR
And I think you want to leave the century off, if you want us to figure it out.
ops$tkyte%ORA10GR2> alter session set nls_date_format = 'dd-mon-yyyy hh24:mi:ss';
Session altered.
ops$tkyte%ORA10GR2> select to_date( '90', 'rr' ) from dual;
TO_DATE('90','RR')
--------------------
01-jan-1990 00:00:00
ops$tkyte%ORA10GR2> select to_date( '10', 'rr' ) from dual;
TO_DATE('10','RR')
--------------------
01-jan-2010 00:00:00
A reader, January 07, 2010 - 8:54 pm UTC
For the above post , this is the corrected SQL.
select to_char(startdate , 'mm/dd/yyyy') sdate, startdate from t
SDATE STARTDATE
-----------------------------------
09/18/2090 18-SEP-90 12.00.00.000000 AM
In the flat file , I have the value as 091890 for the startdate .
January 11, 2010 - 8:51 pm UTC
select to_date( '091890', 'mmddrr' ) from table;
ops$tkyte%ORA10GR2> select to_date( '091890', 'mmddrr' ) from dual
2 /
TO_DATE('091890','MM
--------------------
18-sep-1990 00:00:00
I don't know why you would have
a) '/' in there, you KNOW you don't have any???!?!?!?!
b) use yyyy - you KNOW you have yy at best, and RR is what you want...
To: "A reader" - Re: External table
Kim Berg Hansen, January 12, 2010 - 3:09 am UTC
Hi, "A reader"
You state you use external table to import your flat file. Your flat file have the string "091890". Your "tab" (in your first question, in the second you call it "t") must be your external table, where "startdate" is a timestamp column holding the data from the column in the flat file containing "091890".
What you need to do is to use MMDDRR in your definition of the column "startdate" in your external table. It is the format model in the external table column definition that matters for what century the "startdate" column will have. If you use MMDDYY you will always get "current century", but RR works as Tom says :-)
Difference between two dates grouped by calendar year
Ahmad, January 13, 2010 - 3:05 am UTC
CREATE TABLE EMPS_LEAVE
(
ID NUMBER(10),
LEAVE_ID NUMBER(3) ,
EMP_ID NUMBER(10),
START_DATE DATE ,
END_DATE DATE
);
SET DEFINE OFF;
Insert into MOD.EMPS_LEAVE
(ID, LEAVE_ID, EMP_ID, START_DATE, END_DATE)
Values
(1, 900, 1000, TO_DATE('12/16/2009 00:00:00', 'MM/DD/YYYY HH24:MI:SS'), TO_DATE('01/09/2010 00:00:00', 'MM/DD/YYYY HH24:MI:SS'));
Insert into MOD.EMPS_LEAVE
(ID, LEAVE_ID, EMP_ID, START_DATE, END_DATE)
Values
(2, 900, 2000, TO_DATE('12/12/2009 00:00:00', 'MM/DD/YYYY HH24:MI:SS'), TO_DATE('01/10/2010 00:00:00', 'MM/DD/YYYY HH24:MI:SS'));
Insert into MOD.EMPS_LEAVE
(ID, LEAVE_ID, EMP_ID, START_DATE, END_DATE)
Values
(3, 901, 1000, TO_DATE('01/01/2009 00:00:00', 'MM/DD/YYYY HH24:MI:SS'), TO_DATE('01/31/2009 00:00:00', 'MM/DD/YYYY HH24:MI:SS'));
COMMIT;
how to write a SQL statement to group the difference between the two dates per calendar year .... result set like this
emp_id leave_id duration calendar_year
1000 900 16 2009
1000 900 9 2010
1000 901 31 2009
2000 900 20 2009
2000 900 10 2010
Thanks
January 18, 2010 - 4:13 pm UTC
ops$tkyte%ORA10GR2> with data as (select level r from dual connect by level <= 2)
2 select emp_id, leave_id,
3 decode( r, 1, start_date, trunc(end_date,'y') ) sdate,
4 decode( r, 2, end_date, least( add_months(trunc(start_date,'y'),12)-1, end_date ) ) edate,
5 start_date, end_date
6 from emps_leave, data
7 where trunc(start_date,'y') <> trunc(end_date,'y') or r = 1
8 order by emp_id, leave_id
9 /
EMP_ID LEAVE_ID SDATE EDATE START_DAT END_DATE
---------- ---------- --------- --------- --------- ---------
1000 900 16-DEC-09 31-DEC-09 16-DEC-09 09-JAN-10
1000 900 01-JAN-10 09-JAN-10 16-DEC-09 09-JAN-10
1000 901 01-JAN-09 31-JAN-09 01-JAN-09 31-JAN-09
2000 900 12-DEC-09 31-DEC-09 12-DEC-09 10-JAN-10
2000 900 01-JAN-10 10-JAN-10 12-DEC-09 10-JAN-10
ops$tkyte%ORA10GR2>
ops$tkyte%ORA10GR2> with data as (select level r from dual connect by level <= 2)
2 select emp_id, leave_id, sum(edate-sdate+1) duration, to_char(sdate,'yyyy') calendar_year
3 from (
4 select emp_id, leave_id,
5 decode( r, 1, start_date, trunc(end_date,'y') ) sdate,
6 decode( r, 2, end_date, least( add_months(trunc(start_date,'y'),12)-1, end_date ) ) edate
7 from emps_leave, data
8 where trunc(start_date,'y') <> trunc(end_date,'y') or r = 1
9 )
10 group by emp_id, leave_id, to_char( sdate,'yyyy')
11 order by emp_id, leave_id
12 /
EMP_ID LEAVE_ID DURATION CALE
---------- ---------- ---------- ----
1000 900 16 2009
1000 900 9 2010
1000 901 31 2009
2000 900 20 2009
2000 900 10 2010
note: check the boundary conditions - verify lines 5 and 6 are correct. And if you have to say "but I don't know how to tell if they are correct", you need to sit down first and figure out what this query does :)
basically - turns every row in the original set into one or two rows....
if start date and end date are in the same year, you get one row, else you get two.
then we do magic on the start end dates for row 1 and row 2 to set their values.
then it is simple from there.
Difference between two dates grouped by calendar year
MarkusR, January 15, 2010 - 6:32 am UTC
Hi Ahmed,
maybe this is one solution to your Problem:
SELECT emp_id,
leave_id,
end_date - add_months(start_date, trunc(months_between(end_date, start_date))) + 1 AS duration,
c_year
FROM (SELECT (2008 + LEVEL) AS c_year,
emp_id,
leave_id,
CASE
WHEN start_date > to_date('01/01/' || (2008 + LEVEL), 'MM/DD/YYYY') THEN
start_date
ELSE
to_date('01/01/' || (2008 + LEVEL), 'MM/DD/YYYY')
END AS start_date,
CASE
WHEN end_date > to_date('31.12.' || (2008 + LEVEL), 'DD.MM.YYYY') THEN
to_date('12/31/' || (2008 + LEVEL), 'MM/DD/YYYY')
ELSE
end_date
END AS end_date
FROM emps_leave t
CONNECT BY LEVEL <= 2)
WHERE end_date - add_months(start_date, trunc(months_between(end_date, start_date))) > 0
GROUP BY emp_id, leave_id, end_date - add_months(start_date, trunc(months_between(end_date, start_date))), c_year
order by 1,2,3
EMP_ID LEAVE_ID DURATION YEAR
1000 900 9 2010
1000 900 16 2009
1000 901 31 2009
2000 900 10 2010
2000 900 20 2009
Best regards,
Markus
AW: Difference between two dates grouped by calendar year
MarkusR, January 15, 2010 - 8:51 am UTC
Hi again,
think this is better, sorry for my last Review.
SELECT emp_id,
leave_id,
end_date -
add_months(start_date,
trunc(months_between(end_date,start_date)
)) + 1 AS duration,
c_year
FROM (SELECT (2008 + l_id) AS c_year,
emp_id,
leave_id,
CASE
WHEN start_date > to_date('01/01/' || (2008 + l_id), 'MM/DD/YYYY') THEN
start_date
ELSE
to_date('01/01/' || (2008 + l_id), 'MM/DD/YYYY')
END AS start_date,
CASE
WHEN end_date > to_date('31.12.' || (2008 + l_id), 'DD.MM.YYYY') THEN
to_date('12/31/' || (2008 + l_id), 'MM/DD/YYYY')
ELSE
end_date
END AS end_date
FROM emps_leave t,
(SELECT LEVEL AS l_id
FROM dual
CONNECT BY LEVEL <= 2) g)
WHERE end_date - add_months(start_date, trunc(months_between(end_date, start_date))) > 0
ORDER BY 1,
2,
4
Best regards,
Markus
A reader, March 03, 2010 - 12:37 am UTC
Want to get minimum time portion from "From_Date" and maximum time portion from "To_Date" this should be independent of date only time should be considers.
CREATE TABLE TEST_DATA
(
FROM_DATE DATE,
TO_DATE DATE
);
SET DEFINE OFF;
Insert into TEST_DATA
(FROM_DATE, TO_DATE)
Values
(TO_DATE('03/03/2010 07:00:00', 'MM/DD/YYYY HH24:MI:SS'), TO_DATE('03/03/2010 19:00:00', 'MM/DD/YYYY HH24:MI:SS'));
Insert into TEST_DATA
(FROM_DATE, TO_DATE)
Values
(TO_DATE('12/01/2009 08:00:00', 'MM/DD/YYYY HH24:MI:SS'), TO_DATE('12/15/2009 11:00:00', 'MM/DD/YYYY HH24:MI:SS'));
COMMIT;
March 03, 2010 - 6:59 am UTC
ops$tkyte%ORA10GR2> select min(to_char(from_date, 'hh24:mi:ss')), max(to_char(to_date,'hh24:mi:ss')) from test_data;
MIN(TO_C MAX(TO_C
-------- --------
07:00:00 19:00:00
dates
A reader, March 03, 2010 - 9:16 am UTC
A reader, March 03, 2010 - 9:39 pm UTC
from above table "TEST_DATA" how we can get difference in minutes between from_date and to_date ignorning date part only using time portion of it.
March 04, 2010 - 9:44 am UTC
ops$tkyte%ORA11GR2> alter session set nls_date_format = 'dd-mon-yyyy hh24:mi:ss';
Session altered.
ops$tkyte%ORA11GR2> select from_date, to_date,
2 abs( to_date( to_char(from_date, 'hh24:mi:ss'), 'hh24:mi:ss' )-
3 to_date( to_char( to_date, 'hh24:mi:ss'), 'hh24:mi:ss' ) ) * 24 * 60 mins
4 from test_data;
FROM_DATE TO_DATE MINS
-------------------- -------------------- ----------
03-mar-2010 07:00:00 03-mar-2010 19:00:00 720
01-dec-2009 08:00:00 15-dec-2009 11:00:00 180
03-mar-2010 07:00:00 03-mar-2010 19:00:00 720
01-dec-2009 08:00:00 15-dec-2009 11:00:00 180
03-mar-2010 07:00:00 03-mar-2010 19:00:00 720
01-dec-2009 08:00:00 15-dec-2009 11:00:00 180
6 rows selected.
Date format
Sandeep Deokar, May 31, 2010 - 8:04 am UTC
Hi Tom
I have a multiple upstream and those up streams are sending me date in different format
Eg one is sending me mm/dd/yyyy other is sending me dd/mon/yyyy one is sending me dd/mm/yyyy
My original date format is
SQL> select sysdate from dual;
SYSDATE
---------
31-MAY-10
I have to insert these dates in a column but it is giving me an error
SQL> SELECT to_date(TO_CHAR('1/1/2009'),'DD-Mon-YYYY') FROM DUAL
2 /
SELECT to_date(TO_CHAR('1/1/2009'),'DD-Mon-YYYY') FROM DUAL
*
ERROR at line 1:
ORA-01843: not a valid month
Please suggest what ever is the input it should take date in DD-Mon-YYYY format.
I dont want to write SQL function which will use substr which will check for input dates and then convert in the required date format.
Please suggest.
May 31, 2010 - 8:47 am UTC
My original date format is
SQL> select sysdate from dual;
SYSDATE
---------
31-MAY-10
I see NO date format there, is that dd-mon-yy, fmdd-mon-yyyy, dd-mon-rr - what??
(not relevant to your question, just saying - there is no date format in your example, none, you are as ambiguous as everyone else)
You need to use a date format of mm/dd/yyyy for the data from one system.
and dd/mon/yyyy from the other
and dd/mm/yyyy from yet the other
In fact, I would suggest using FXmm/dd/yyyy to avoid data errors for the one
fxdd/mon/yyyy
fxdd/mm/yyyy
I dont want to write SQL function which will use substr which will check for
input dates and then convert in the required date format.
good because that would be totally lame, you need to develop a framework for your application that knows that "if the data comes from, then format is A, if from Y then format is B and so on"
so you can add new formats, change them, whatever over time.
Week
A reader, September 28, 2010 - 3:58 am UTC
in first respone of this question you are calculating YEARS MONTHS DAYS i need these
YEARS MONTHS WEEKS DAYS
How can i?
September 28, 2010 - 8:23 am UTC
think about it and start dividing ....
take the work I've performed which demonstrates a concept (date arithmetic) and take it one step further to accomplish your unique and specific needs. that is what programmers do.
Learn and understand the technique (you see me adding and subtracting and dividing - that is all you need to do) and apply it to your problem
Look at my first example, one of the rows had 23 days. If you take 23 and divide by 7 - you get 3.something. Truncate it, that is three weeks. Now if you subtract from 23 - 3*7 you get 2. You have 3 weeks and 2 days...
not working
venkata, July 21, 2011 - 2:35 am UTC
July 22, 2011 - 1:46 pm UTC
Tom you are great
Muzafar Jatoi, July 27, 2011 - 3:19 am UTC
Tom,
You are last hope for all DBA & Developers.
You help many yong DBA & Developers.
I solute you on your best work to help others getting time from your busy schedule.
Regards,
Muzafar
Oracle DBA
Negative number of days returned
Livio, February 06, 2013 - 7:44 am UTC
Hello Tom,
I would like to come back to one of the comments at the beginning of this thread, namely the fact that the function that returns the difference between two dates may as well return a negative number of days.
I have been trying out this alternative
select extract(year from years_month) yy, extract(month from years_month) mm, extract(day from days_hours)dd
from (
select numtoyminterval(trunc(months_between(dt2, dt1)), 'month') years_month,
numtodsinterval(dt2-add_months(dt1, trunc(months_between(dt2, dt1))), 'day') days_hours
from (
select to_date('28/02/2002', 'dd/mm/yyyy') dt1, to_date('27/03/2004', 'dd/mm/yyyy') + 1 dt2
from dual
)
)
but with no success so far, with the provided data.
I wonder if there is any solution or workaround. Could you provide it or direct me to the relevant documentation?
Thanks in advance for your help.
Livio
February 06, 2013 - 2:05 pm UTC
what do you think the answer should be refer back to my comments above, what is the *right* answer)
I think you just take the greatest( 0, days ).
our "convention" is the same day of two different months is 1 month. 15-feb to 15-mar is one month. so is the last day of a month to the next - 28-feb to 31-mar is one month. Months and days don't "go together" well.
tell me - what is the right answer and why ?
Negative number of days returned for adjacent days
Livio, February 07, 2013 - 3:38 am UTC
Hello Tom.
Thank you for your reply.
The following table recaps what's puzzling me.
Start date End date Years Months Days Mycomment
28-02-2002 26-03-2004 2 0 27 OK
28-02-2002 27-03-2004 2 1 -3 ? (0 m 28 d)
28-02-2002 28-03-2004 2 1 -2 ? (0 m 29 d)
28-02-2002 29-03-2004 2 1 -1 ? (0 m 30 d)
28-02-2002 30-03-2004 2 1 0 OK
28-02-2002 31-03-2004 2 1 1 OK
28-02-2002 01-04-2004 2 1 2 OK
You see, by simply zeroing out negative days, we get the same number of days for March 27, 28, 29 and 30. That doesn't seem reasonable to me, does it? I would expect the results put in brackets or, if a new month is supposed to start at 27-03-2004, the following day should report a day more and so on.
Maybe this is an issue that has to do with leap years (in fact, February 2004 was a leap year)
Thank you in advance for your feedback
Best regards
Livio
February 11, 2013 - 8:07 am UTC
what is the result you would like?
the problem lies in the fact that a month is a nebulous thing - it is not a precise number of days.
Feb-N to Mar-N is commonly accepted as a "month". for example Feb-15th to Mar-15th is a month.
So, Feb-28 to Mar-28 is a month.
last_day(feb) to last_day(mar) is also commonly accepted as a "month". So Feb-28 (non-leap year) to Mar-31 is a month.
So, should Feb-28 to Mar 28 be a month and then Feb-28 to Mar-29 be a month and a day? and Feb-28 to Mar-30 a month and two days? and then Feb-28 to Mar-31 a month again? That doesn't seem reasonable to me.
of Feb-28 to Mar 28 be a month and then Feb-28 to Mar-29 be 30 days again? not a month? but it has to be a month of feb-15 to mar-15 is considered a month.
but remember that feb-28 to mar-28 has to be a month, just like feb-27 to mar-27 is a month.
tell me what you would like and we'll get you that answer. I say just zero out the negative days. It is as accurate and meaningful as any other answer.
Negative number of days returned
Livio, February 12, 2013 - 10:26 am UTC
Hello Tom.
Firstly, thanks for taking the time to reply.
The following table summarizes what I want.
Start date End date Years Months Days
28-02-2002 26-03-2004 2 0 27
28-02-2002 27-03-2004 2 0 28
28-02-2002 28-03-2004 2 0 29
28-02-2002 29-03-2004 2 0 30
28-02-2002 30-03-2004 2 1 0
28-02-2002 31-03-2004 2 1 1
28-02-2002 01-04-2004 2 1 2
So I came up with the following query:
SELECT yy,
CASE WHEN dd < 0 THEN GREATEST(0, mm - 1) ELSE mm END mm,
CASE WHEN dd < 0 THEN TO_NUMBER(TO_CHAR(end_of_month, 'dd')) + dd ELSE dd END dd
FROM (
SELECT EXTRACT(YEAR FROM years_month) yy, EXTRACT(MONTH FROM years_month) mm, EXTRACT(DAY FROM days_hours)dd, end_of_month
FROM (
SELECT NUMTOYMINTERVAL(TRUNC(MONTHS_BETWEEN(dt2, dt1)), 'month') years_month,
NUMTODSINTERVAL(dt2 - ADD_MONTHS(dt1, TRUNC(MONTHS_BETWEEN(dt2, dt1))), 'day') days_hours, LAST_DAY(dt2) end_of_month
FROM (SELECT p_start_date dt1, p_end_date dt2 FROM dual)
)
);
I will appreciate any feedback.
What puzzles me about zeroing out the number of negative days is that we can have the same reported result for different days.
Best regards
Livio
February 12, 2013 - 11:17 am UTC
so, how do you rectify that feb-15 to mar-15 is a month
but feb-28 to mar-28 is not a month?
that goes against all of the rules.
... What puzzles me about zeroing out the number of negative days is that we can
have the same reported result for different days. ...
that should be obvious - a month is not a fixed period of time!!!!
If someone says "see you in a month" - how many days is that?
Getting First Day of all the months between two years
Suma, June 10, 2013 - 2:37 am UTC
Hi Tom
I am new to these date functions, kindly help me
I am giving input as Two years(Fromyear and Toyear),
Between these two years I need to get the first date of all the months which fall in between these two years.
01-05-2012 (mm-dd-yy) as Fromyear
01-05-2013 (mm-dd-yy) as Toyear
My result should be
01-05-2012
01-06-2012
01-07-2012
till 01-05-2013
Please do help me.
Thanks
Suma.
June 18, 2013 - 2:31 pm UTC
you ask for the first day but then post the 5th, 6th, 7th? I'm not sure what your output should be - I'll go with the first day of each month.
ops$tkyte%ORA11GR2> variable sdate varchar2(30)
ops$tkyte%ORA11GR2> variable edate varchar2(30)
ops$tkyte%ORA11GR2>
ops$tkyte%ORA11GR2> exec :sdate := '01-05-2012'; :edate := '01-06-2013';
PL/SQL procedure successfully completed.
ops$tkyte%ORA11GR2>
ops$tkyte%ORA11GR2> select add_months( trunc( sdate, 'mm' ), level-1 ), edate
2 from (
3 select to_date( :sdate, 'mm-dd-yyyy' ) sdate, to_date( :edate, 'mm-dd-yyyy' ) edate
4 from dual
5 )
6 where add_months( trunc(sdate,'mm'), level-1) <= edate
7 connect by level <= ceil( months_between( edate, sdate ) )+1
8 /
ADD_MONTH EDATE
--------- ---------
01-JAN-12 06-JAN-13
01-FEB-12 06-JAN-13
01-MAR-12 06-JAN-13
01-APR-12 06-JAN-13
01-MAY-12 06-JAN-13
01-JUN-12 06-JAN-13
01-JUL-12 06-JAN-13
01-AUG-12 06-JAN-13
01-SEP-12 06-JAN-13
01-OCT-12 06-JAN-13
01-NOV-12 06-JAN-13
01-DEC-12 06-JAN-13
01-JAN-13 06-JAN-13
13 rows selected.
driver sharp
Abirkhan, August 26, 2022 - 7:56 pm UTC
like ID on Bank
August 30, 2022 - 1:30 am UTC
o.....k......
?
Leap years and new case
Alkhalili, December 10, 2024 - 11:26 am UTC
Assuming that date1 and date2 are included in the period , try a period from 13-12-2023 till 12-12-2024, (366) days.
The query will return 11.97 months , 0 years , 11 months and 29 days.
December 11, 2024 - 6:11 am UTC
When you deal with months, plenty of challenges can come up
Using intervals
Stew Ashton, December 12, 2024 - 6:26 pm UTC
The difference between two dates should allow us to do round trips; in other words, the start date plus the difference should return the end date and the end date minus the difference should return the start date.
Using ADD_MONTHS we lose information: for example, add a month to any date from January 28th on and we get the same result, so subtracting a month doesn't get us back to the original start date.
Intervals work differently: they don't allow us to add a month to January 31st.
Here is a solution using interval expressions that handles dates or timestamps and allows round trips:
1) If the "from" day-to-second portion is not greater than the "to" portion, is it easy to separate a YEAR TO MONTH portion and a DAY TO SECOND portion
2) In other cases, whenever possible subtract a month from the YEAR TO MONTH portion and add to the DAY TO SECOND portion the number of days in the month
preceding the "to" date.
3) When 2) is not an option, leave the portions as is and accept that the day-to-second portion will be negative!
To make testing easier, here is a view that generates the question
create or replace view question as
with dates(dte) as (
select date '2023-01-26' + numtodsinterval(level,'day')
from dual
connect by level <= 35
)
, ranges(date_from, date_to) as (
select a.dte, b.dte + 1.31/24
from dates a, dates b
where a.dte < b.dte - 25
)
select * from ranges
order by 1,2;
In the answer, I separate the different date/time portions and I recalculate the from and to dates.
with data as (
select date_from, date_to,
(trunc(date_to,'mm') - trunc(date_from,'mm')) year(5) to month iym,
(date_from - trunc(date_from, 'mm')) day to second from_ids,
(date_to - trunc(date_to, 'mm')) day to second to_ids,
extract(day from date_from) from_day,
extract(day from trunc(date_to, 'mm') - 1) prev_to_day
from question
)
, new_intervals as (
select date_from, date_to,
case when from_ids > to_ids and from_day <= prev_to_day
then (iym - interval '1' month)
else iym
end new_iym,
case when from_ids > to_ids and from_day <= prev_to_day
then to_ids - from_ids + numtodsinterval(prev_to_day, 'day')
else to_ids - from_ids
end new_ids
from data
)
select date_from, date_to,
extract(year from new_iym) yy,
extract(month from new_iym) mm,
extract(day from new_ids) dd,
extract(hour from new_ids) hh24,
extract(minute from new_ids) mi,
extract(second from new_ids) ss,
date_to - new_ids - new_iym same_date_from,
date_from + new_iym + new_ids same_date_to
from new_intervals
order by date_from, date_to
DATE_FROM DATE_TO YY MM DD HH24 MI SS SAME_DATE_FROM SAME_DATE_TO
2023-01-27 00:00:00 2023-02-22 01:18:36 0 0 26 1 18 36 2023-01-27 00:00:00 2023-02-22 01:18:36
2023-01-27 00:00:00 2023-02-23 01:18:36 0 0 27 1 18 36 2023-01-27 00:00:00 2023-02-23 01:18:36
2023-01-27 00:00:00 2023-02-24 01:18:36 0 0 28 1 18 36 2023-01-27 00:00:00 2023-02-24 01:18:36
2023-01-27 00:00:00 2023-02-25 01:18:36 0 0 29 1 18 36 2023-01-27 00:00:00 2023-02-25 01:18:36
2023-01-27 00:00:00 2023-02-26 01:18:36 0 0 30 1 18 36 2023-01-27 00:00:00 2023-02-26 01:18:36
2023-01-27 00:00:00 2023-02-27 01:18:36 0 1 0 1 18 36 2023-01-27 00:00:00 2023-02-27 01:18:36
2023-01-27 00:00:00 2023-02-28 01:18:36 0 1 1 1 18 36 2023-01-27 00:00:00 2023-02-28 01:18:36
2023-01-27 00:00:00 2023-03-01 01:18:36 0 1 2 1 18 36 2023-01-27 00:00:00 2023-03-01 01:18:36
2023-01-27 00:00:00 2023-03-02 01:18:36 0 1 3 1 18 36 2023-01-27 00:00:00 2023-03-02 01:18:36
2023-01-28 00:00:00 2023-02-23 01:18:36 0 0 26 1 18 36 2023-01-28 00:00:00 2023-02-23 01:18:36
2023-01-28 00:00:00 2023-02-24 01:18:36 0 0 27 1 18 36 2023-01-28 00:00:00 2023-02-24 01:18:36
2023-01-28 00:00:00 2023-02-25 01:18:36 0 0 28 1 18 36 2023-01-28 00:00:00 2023-02-25 01:18:36
2023-01-28 00:00:00 2023-02-26 01:18:36 0 0 29 1 18 36 2023-01-28 00:00:00 2023-02-26 01:18:36
2023-01-28 00:00:00 2023-02-27 01:18:36 0 0 30 1 18 36 2023-01-28 00:00:00 2023-02-27 01:18:36
2023-01-28 00:00:00 2023-02-28 01:18:36 0 1 0 1 18 36 2023-01-28 00:00:00 2023-02-28 01:18:36
2023-01-28 00:00:00 2023-03-01 01:18:36 0 1 1 1 18 36 2023-01-28 00:00:00 2023-03-01 01:18:36
2023-01-28 00:00:00 2023-03-02 01:18:36 0 1 2 1 18 36 2023-01-28 00:00:00 2023-03-02 01:18:36
2023-01-29 00:00:00 2023-02-24 01:18:36 0 0 26 1 18 36 2023-01-29 00:00:00 2023-02-24 01:18:36
2023-01-29 00:00:00 2023-02-25 01:18:36 0 0 27 1 18 36 2023-01-29 00:00:00 2023-02-25 01:18:36
2023-01-29 00:00:00 2023-02-26 01:18:36 0 0 28 1 18 36 2023-01-29 00:00:00 2023-02-26 01:18:36
2023-01-29 00:00:00 2023-02-27 01:18:36 0 0 29 1 18 36 2023-01-29 00:00:00 2023-02-27 01:18:36
2023-01-29 00:00:00 2023-02-28 01:18:36 0 0 30 1 18 36 2023-01-29 00:00:00 2023-02-28 01:18:36
2023-01-29 00:00:00 2023-03-01 01:18:36 0 2 -27 -22 -41 -24 2023-01-29 00:00:00 2023-03-01 01:18:36
2023-01-29 00:00:00 2023-03-02 01:18:36 0 2 -26 -22 -41 -24 2023-01-29 00:00:00 2023-03-02 01:18:36
2023-01-30 00:00:00 2023-02-25 01:18:36 0 0 26 1 18 36 2023-01-30 00:00:00 2023-02-25 01:18:36
2023-01-30 00:00:00 2023-02-26 01:18:36 0 0 27 1 18 36 2023-01-30 00:00:00 2023-02-26 01:18:36
2023-01-30 00:00:00 2023-02-27 01:18:36 0 0 28 1 18 36 2023-01-30 00:00:00 2023-02-27 01:18:36
2023-01-30 00:00:00 2023-02-28 01:18:36 0 0 29 1 18 36 2023-01-30 00:00:00 2023-02-28 01:18:36
2023-01-30 00:00:00 2023-03-01 01:18:36 0 2 -28 -22 -41 -24 2023-01-30 00:00:00 2023-03-01 01:18:36
2023-01-30 00:00:00 2023-03-02 01:18:36 0 2 -27 -22 -41 -24 2023-01-30 00:00:00 2023-03-02 01:18:36
2023-01-31 00:00:00 2023-02-26 01:18:36 0 0 26 1 18 36 2023-01-31 00:00:00 2023-02-26 01:18:36
2023-01-31 00:00:00 2023-02-27 01:18:36 0 0 27 1 18 36 2023-01-31 00:00:00 2023-02-27 01:18:36
2023-01-31 00:00:00 2023-02-28 01:18:36 0 0 28 1 18 36 2023-01-31 00:00:00 2023-02-28 01:18:36
2023-01-31 00:00:00 2023-03-01 01:18:36 0 2 -29 -22 -41 -24 2023-01-31 00:00:00 2023-03-01 01:18:36
2023-01-31 00:00:00 2023-03-02 01:18:36 0 2 -28 -22 -41 -24 2023-01-31 00:00:00 2023-03-02 01:18:36
2023-02-01 00:00:00 2023-02-27 01:18:36 0 0 26 1 18 36 2023-02-01 00:00:00 2023-02-27 01:18:36
2023-02-01 00:00:00 2023-02-28 01:18:36 0 0 27 1 18 36 2023-02-01 00:00:00 2023-02-28 01:18:36
2023-02-01 00:00:00 2023-03-01 01:18:36 0 1 0 1 18 36 2023-02-01 00:00:00 2023-03-01 01:18:36
2023-02-01 00:00:00 2023-03-02 01:18:36 0 1 1 1 18 36 2023-02-01 00:00:00 2023-03-02 01:18:36
2023-02-02 00:00:00 2023-02-28 01:18:36 0 0 26 1 18 36 2023-02-02 00:00:00 2023-02-28 01:18:36
2023-02-02 00:00:00 2023-03-01 01:18:36 0 0 27 1 18 36 2023-02-02 00:00:00 2023-03-01 01:18:36
2023-02-02 00:00:00 2023-03-02 01:18:36 0 1 0 1 18 36 2023-02-02 00:00:00 2023-03-02 01:18:36
2023-02-03 00:00:00 2023-03-01 01:18:36 0 0 26 1 18 36 2023-02-03 00:00:00 2023-03-01 01:18:36
2023-02-03 00:00:00 2023-03-02 01:18:36 0 0 27 1 18 36 2023-02-03 00:00:00 2023-03-02 01:18:36
2023-02-04 00:00:00 2023-03-02 01:18:36 0 0 26 1 18 36 2023-02-04 00:00:00 2023-03-02 01:18:36
December 13, 2024 - 5:44 pm UTC
Thanks for sharing Stew; shows there are many ways to solve this problem.
Alternative using intervals
Stew Ashton, December 13, 2024 - 9:09 am UTC
Here is a somewhat different solution (using "start_date , end_date" instead of "date_from, date_to"):
1) If start_date is earlier
in its month than end_date is in its month, simply separate the YEAR TO MONTH difference and the DAY TO SECOND difference
2) Or if start_date is earlier in its month than the last day of the month
prior to end_date, then subtract a month from the YEAR TO MONTH difference and add the prior month's days to the DAY TO SECOND difference
3) Otherwise, calculate the differences starting from the
end_date, not the start_date. The YEAR TO MONTH difference will be negative, and round trips require applying the differences in another order.
create or replace view question as
with dates(dte) as (
select date '2023-01-26' + numtodsinterval(level,'day')
from dual
connect by level <= 35
)
, ranges(start_date, end_date) as (
select a.dte, b.dte + 1/3
from dates a, dates b
where a.dte < b.dte - 25
)
select * from ranges
order by 1,2;
with data as (
select start_date, end_date,
(trunc(end_date,'mm') - trunc(start_date,'mm')) year(5) to month iym,
(trunc(start_date,'mm') - trunc(end_date,'mm')) year(5) to month reverse_iym,
(start_date - trunc(start_date, 'mm')) day to second start_ids,
(end_date - trunc(end_date, 'mm')) day to second end_ids,
extract(day from start_date) start_day,
extract(day from trunc(end_date, 'mm') - 1) prev_last_day
from question
)
, new_intervals as (
select start_date, end_date, start_ids, end_ids, start_day, prev_last_day,
case
when start_ids <= end_ids
then iym
when start_day <= prev_last_day
then (iym - interval '1' month)
else reverse_iym
end new_iym,
case
when start_ids <= end_ids
then end_ids - start_ids
when start_day <= prev_last_day
then end_ids - start_ids + numtodsinterval(prev_last_day, 'day')
else start_ids - end_ids
end new_ids
from data
)
select start_date, end_date,
extract(year from new_iym) yy,
extract(month from new_iym) mm,
extract(day from new_ids) dd,
extract(hour from new_ids) hh24,
extract(minute from new_ids) mi,
extract(second from new_ids) ss,
case when start_ids > end_ids and start_day > prev_last_day
then end_date + new_iym + new_ids
else end_date - new_ids - new_iym
end same_start_date,
case when start_ids > end_ids and start_day > prev_last_day
then start_date - new_ids - new_iym
else start_date + new_iym + new_ids
end same_end_date
from new_intervals
order by start_date, end_date
/
START_DATE END_DATE YY MM DD HH24 MI SS SAME_START_DATE SAME_END_DATE
2023-01-27 00:00:00 2023-02-22 08:00:00 0 0 26 8 0 0 2023-01-27 00:00:00 2023-02-22 08:00:00
2023-01-27 00:00:00 2023-02-23 08:00:00 0 0 27 8 0 0 2023-01-27 00:00:00 2023-02-23 08:00:00
2023-01-27 00:00:00 2023-02-24 08:00:00 0 0 28 8 0 0 2023-01-27 00:00:00 2023-02-24 08:00:00
2023-01-27 00:00:00 2023-02-25 08:00:00 0 0 29 8 0 0 2023-01-27 00:00:00 2023-02-25 08:00:00
2023-01-27 00:00:00 2023-02-26 08:00:00 0 0 30 8 0 0 2023-01-27 00:00:00 2023-02-26 08:00:00
2023-01-27 00:00:00 2023-02-27 08:00:00 0 1 0 8 0 0 2023-01-27 00:00:00 2023-02-27 08:00:00
2023-01-27 00:00:00 2023-02-28 08:00:00 0 1 1 8 0 0 2023-01-27 00:00:00 2023-02-28 08:00:00
2023-01-27 00:00:00 2023-03-01 08:00:00 0 1 2 8 0 0 2023-01-27 00:00:00 2023-03-01 08:00:00
2023-01-27 00:00:00 2023-03-02 08:00:00 0 1 3 8 0 0 2023-01-27 00:00:00 2023-03-02 08:00:00
2023-01-28 00:00:00 2023-02-23 08:00:00 0 0 26 8 0 0 2023-01-28 00:00:00 2023-02-23 08:00:00
2023-01-28 00:00:00 2023-02-24 08:00:00 0 0 27 8 0 0 2023-01-28 00:00:00 2023-02-24 08:00:00
2023-01-28 00:00:00 2023-02-25 08:00:00 0 0 28 8 0 0 2023-01-28 00:00:00 2023-02-25 08:00:00
2023-01-28 00:00:00 2023-02-26 08:00:00 0 0 29 8 0 0 2023-01-28 00:00:00 2023-02-26 08:00:00
2023-01-28 00:00:00 2023-02-27 08:00:00 0 0 30 8 0 0 2023-01-28 00:00:00 2023-02-27 08:00:00
2023-01-28 00:00:00 2023-02-28 08:00:00 0 1 0 8 0 0 2023-01-28 00:00:00 2023-02-28 08:00:00
2023-01-28 00:00:00 2023-03-01 08:00:00 0 1 1 8 0 0 2023-01-28 00:00:00 2023-03-01 08:00:00
2023-01-28 00:00:00 2023-03-02 08:00:00 0 1 2 8 0 0 2023-01-28 00:00:00 2023-03-02 08:00:00
2023-01-29 00:00:00 2023-02-24 08:00:00 0 0 26 8 0 0 2023-01-29 00:00:00 2023-02-24 08:00:00
2023-01-29 00:00:00 2023-02-25 08:00:00 0 0 27 8 0 0 2023-01-29 00:00:00 2023-02-25 08:00:00
2023-01-29 00:00:00 2023-02-26 08:00:00 0 0 28 8 0 0 2023-01-29 00:00:00 2023-02-26 08:00:00
2023-01-29 00:00:00 2023-02-27 08:00:00 0 0 29 8 0 0 2023-01-29 00:00:00 2023-02-27 08:00:00
2023-01-29 00:00:00 2023-02-28 08:00:00 0 0 30 8 0 0 2023-01-29 00:00:00 2023-02-28 08:00:00
2023-01-29 00:00:00 2023-03-01 08:00:00 0 -2 27 16 0 0 2023-01-29 00:00:00 2023-03-01 08:00:00
2023-01-29 00:00:00 2023-03-02 08:00:00 0 -2 26 16 0 0 2023-01-29 00:00:00 2023-03-02 08:00:00
2023-01-30 00:00:00 2023-02-25 08:00:00 0 0 26 8 0 0 2023-01-30 00:00:00 2023-02-25 08:00:00
2023-01-30 00:00:00 2023-02-26 08:00:00 0 0 27 8 0 0 2023-01-30 00:00:00 2023-02-26 08:00:00
2023-01-30 00:00:00 2023-02-27 08:00:00 0 0 28 8 0 0 2023-01-30 00:00:00 2023-02-27 08:00:00
2023-01-30 00:00:00 2023-02-28 08:00:00 0 0 29 8 0 0 2023-01-30 00:00:00 2023-02-28 08:00:00
2023-01-30 00:00:00 2023-03-01 08:00:00 0 -2 28 16 0 0 2023-01-30 00:00:00 2023-03-01 08:00:00
2023-01-30 00:00:00 2023-03-02 08:00:00 0 -2 27 16 0 0 2023-01-30 00:00:00 2023-03-02 08:00:00
2023-01-31 00:00:00 2023-02-26 08:00:00 0 0 26 8 0 0 2023-01-31 00:00:00 2023-02-26 08:00:00
2023-01-31 00:00:00 2023-02-27 08:00:00 0 0 27 8 0 0 2023-01-31 00:00:00 2023-02-27 08:00:00
2023-01-31 00:00:00 2023-02-28 08:00:00 0 0 28 8 0 0 2023-01-31 00:00:00 2023-02-28 08:00:00
2023-01-31 00:00:00 2023-03-01 08:00:00 0 -2 29 16 0 0 2023-01-31 00:00:00 2023-03-01 08:00:00
2023-01-31 00:00:00 2023-03-02 08:00:00 0 -2 28 16 0 0 2023-01-31 00:00:00 2023-03-02 08:00:00
2023-02-01 00:00:00 2023-02-27 08:00:00 0 0 26 8 0 0 2023-02-01 00:00:00 2023-02-27 08:00:00
2023-02-01 00:00:00 2023-02-28 08:00:00 0 0 27 8 0 0 2023-02-01 00:00:00 2023-02-28 08:00:00
2023-02-01 00:00:00 2023-03-01 08:00:00 0 1 0 8 0 0 2023-02-01 00:00:00 2023-03-01 08:00:00
2023-02-01 00:00:00 2023-03-02 08:00:00 0 1 1 8 0 0 2023-02-01 00:00:00 2023-03-02 08:00:00
2023-02-02 00:00:00 2023-02-28 08:00:00 0 0 26 8 0 0 2023-02-02 00:00:00 2023-02-28 08:00:00
2023-02-02 00:00:00 2023-03-01 08:00:00 0 0 27 8 0 0 2023-02-02 00:00:00 2023-03-01 08:00:00
2023-02-02 00:00:00 2023-03-02 08:00:00 0 1 0 8 0 0 2023-02-02 00:00:00 2023-03-02 08:00:00
2023-02-03 00:00:00 2023-03-01 08:00:00 0 0 26 8 0 0 2023-02-03 00:00:00 2023-03-01 08:00:00
2023-02-03 00:00:00 2023-03-02 08:00:00 0 0 27 8 0 0 2023-02-03 00:00:00 2023-03-02 08:00:00
2023-02-04 00:00:00 2023-03-02 08:00:00 0 0 26 8 0 0 2023-02-04 00:00:00 2023-03-02 08:00:00
This solution supersedes my previous comments
Stew Ashton, January 09, 2025 - 11:28 am UTC
Sorry for my previous imperfect solutions which I can't delete.
I think the requirement should be:
1) get the difference between two datetime (date or timestamp) values, expressed in years, months, days, hours, minutes and seconds;
2) calendar years and months;
3) non-negative integers, except that seconds can have fractions;
4) maximum values: for months 11, for days 30, for hours 23, for minutes and seconds 59 (plus the fractional seconds);
5) we should be able to calculate one date from the other and the difference ("round trip").
As Tom wrote on August 10, 2005 - 10:34 am UTC, requirements 1, 2 and 4 can be handled by using one "year to month" interval and one "day to second" interval. But he uses ADD_MONTHS, which only works with dates, so we lose any fractional seconds. Also, it sometimes changes the day of the month, which makes round trips impossible.
Anyway, his solution gives wrong answers, such as:
SQL> select
2 numtoyminterval (months_between(dt2,dt1),'month') years_months,
3 numtodsinterval(dt2-add_months(dt1, trunc(months_between(dt2,dt1))), 'day') days_hours
4 from (
5 select
6 to_date('2023-02-03 00:00:00','yyyy-mm-dd hh24:mi:ss') dt1,
7 to_date('2023-04-02 00:00:00','yyyy-mm-dd hh24:mi:ss') dt2
8 from dual
9 );
YEARS_ DAYS_HOURS
------ -------------------
+00-02 +30 00:00:00.000000
Using only "interval expressions" we can accept as input either dates or timestamps without data loss and without changing the day of the month.
To get the difference between February 3d and April 2d, I add 26 days to get March 1st, then 1 month to get April 1st, then 1 day to get April 2d. The total is 1 month (March) and 27 days.
Unfortunately, I can also add 1 month to get to March 3d, then 27 days to get to March 30th! The total is also 1 month (February) and 27 days.
To tell the two apart, I need one additional bit of information: was the starting "day of the month" greater than the ending "day of the month"?
SQL> with data(start_datetime, end_datetime) as (
2 select date '2023-02-03', timestamp '2023-03-30 00:00:00'
3 from dual
4 union all
5 select date '2023-02-03', timestamp '2023-04-02 00:00:00'
6 from dual
7 ), dates as (
8 select start_datetime, end_datetime,
9 trunc(start_datetime,'mm') start_month,
10 trunc(start_datetime,'mm') + interval '1' month start_next_month,
11 trunc(end_datetime,'mm') end_month
12 from data
13 )
14 , intervals as (
15 select start_datetime, end_datetime,
16 (end_month - start_month) year to month full_iym,
17 (end_month - start_next_month) year to month next_iym,
18 (start_datetime - start_month) day to second start_ids,
19 (start_next_month - start_datetime) day to second start_to_next_ids,
20 (end_datetime - end_month) day to second end_ids
21 from dates
22 )
23 select start_datetime, end_datetime,
24 case
25 when start_ids > end_ids then next_iym
26 else full_iym
27 end iym,
28 case
29 when start_ids > end_ids then start_to_next_ids + end_ids
30 else end_ids - start_ids
31 end ids,
32 case
33 when start_ids > end_ids then 1
34 else 0
35 end start_gt_end
36 from intervals;
START_DATETIME END_DATETIME IYM IDS START_GT_END
------------------- --------------------- ------ ------------------- ------------
2023-02-03 00:00:00 2023-03-30 00:00:00.0 +00-01 +27 00:00:00.000000 0
2023-02-03 00:00:00 2023-04-02 00:00:00.0 +00-01 +27 00:00:00.000000 1
Knowing whether start_ids was greater than end_ids, I can easily reverse the logic and calculate either date from the other date and the difference.
January 09, 2025 - 12:09 pm UTC
Thanks for sharing Stew
http://asktom.oracle.com/Misc/DateDiff.html is obsolete
Stew Ashton, January 09, 2025 - 1:41 pm UTC
http://asktom.oracle.com/Misc/DateDiff.html is obsolete, not to mention the occasional slight rounding error that Tom once admitted.
The requirement is to find the difference between two datetime values in days, hours, minutes and seconds (I would add fractional seconds).
For some time, the best solution has simply been
(end_datetime - start_datetime) day to second
SQL> with data(end_datetime, start_datetime) as (
2 select localtimestamp, current_date - 1.2345
3 )
4 select end_datetime, start_datetime,
5 (end_datetime - start_datetime) day to second datediff
6 from data;
END_DATETIME START_DATETIME DATEDIFF
----------------------------- ------------------- -------------------
2025-01-09 14:28:21,022831000 2025-01-08 08:50:40 +01 05:37:41.022831
We can mix timestamps and dates at will. Also, we can tell from the minus sign which value should come first, without having to double-check the documentation. The result can immediately be used to calculate one datetime from the other datetime and the difference.
If anyone wants to separate days, hours, minutes and seconds then they can use the EXTRACT function.
January 13, 2025 - 4:25 am UTC
We'll update the original answer.
Thanks