SQL Server: determining the Years, Months, Weeks and Days between two dates - sql

After reading on this topic and being advised to use DateDiff. I wrote a function that doesn't provide the answer I want. The client wants to now how long it took to complete a checklist. I have a CreationDate and CompletionDate. I need to know how many years, months, weeks and days it took. If it is 2 days then '2 days' without the years. The function deducts the number of years and then attempt to check the number of months, then the number of weeks and then the number of days. Only results are given if available. It seems DateDiff is the problem... or I am the problem not understanding DateDiff. It even returns a week for a 4 day difference in dates which doesn't make sense. It should return the number of weeks within the two dates, not caring when it starts.
This is the code
ALTER FUNCTION [dbo].[DateRangeText]
(#FromDate DATETIME, #ToDate DATETIME)
RETURNS VARCHAR(MAX)
AS
BEGIN
DECLARE #Result AS VARCHAR(MAX);
SET #Result = '';
DECLARE #TmpS AS VARCHAR(MAX);
SET #TmpS = '';
DECLARE #Years AS INT;
SET #Years = DATEDIFF(year, #FromDate, #ToDate);
IF (#Years > 0)
BEGIN
IF (#Years = 1)
SET #TmpS = ' Year ';
ELSE
SET #TmpS = ' Years ';
SET #Result = #Result + CAST(#Years AS VARCHAR) + #TmpS;
SET #ToDate = DATEADD(YEAR, -1 * #Years, #ToDate);
END;
DECLARE #Months AS INT;
SET #Months = DATEDIFF(month, #FromDate, #ToDate);
IF (#Months > 0)
BEGIN
IF (#Months = 1)
SET #TmpS = ' Month ';
ELSE
SET #TmpS = ' Months ';
SET #Result = #Result + CAST(#Months AS VARCHAR) + #TmpS;
SET #ToDate = DATEADD(MONTH, -1 * #Months, #ToDate);
END;
DECLARE #Weeks AS INT;
SET #Weeks = DATEDIFF(week, #FromDate, #ToDate);
IF (#Weeks > 0)
BEGIN
IF (#Weeks = 1)
SET #TmpS = ' Week ';
ELSE
SET #TmpS = ' Weeks ';
SET #Result = #Result + CAST(#Weeks AS VARCHAR) + #TmpS;
SET #ToDate = DATEADD(WEEK, -1 * #Weeks, #ToDate);
END;
DECLARE #Days AS INT;
SET #Days = DATEDIFF(day, #FromDate, #ToDate);
IF (#Days > 0)
BEGIN
IF (#Days = 1)
SET #TmpS = ' Day ';
ELSE
SET #TmpS = ' Days ';
SET #Result = #Result + CAST(#Days AS VARCHAR) + #TmpS;
SET #ToDate = DATEADD(WEEK, -1 * #Days, #ToDate);
END;
IF (#Result = '')
SET #Result = 'Same day';
RETURN Rtrim(COALESCE(#Result,''));
END;

Since you are using a function, consider the following Table-Valued Function. Easy to use a stand-alone or included as a CROSS APPLY.
Performant and Accurate without having to worry about all the misc date calculations.
Example
Select * from [dbo].[tvf-Date-Elapsed] ('1991-09-12 21:00:00.000',GetDate())
Returns
Years Months Days Hours Minutes Seconds
26 7 5 13 47 11
The TVF if interested
CREATE FUNCTION [dbo].[tvf-Date-Elapsed] (#D1 DateTime,#D2 DateTime)
Returns Table
Return (
with cteBN(N) as (Select 1 From (Values(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) N(N)),
cteRN(R) as (Select Row_Number() Over (Order By (Select NULL))-1 From cteBN a,cteBN b,cteBN c),
cteYY(N,D) as (Select Max(R),Max(DateAdd(YY,R,#D1))From cteRN R Where DateAdd(YY,R,#D1)<=#D2),
cteMM(N,D) as (Select Max(R),Max(DateAdd(MM,R,D)) From (Select Top 12 R From cteRN Order By 1) R, cteYY P Where DateAdd(MM,R,D)<=#D2),
cteDD(N,D) as (Select Max(R),Max(DateAdd(DD,R,D)) From (Select Top 31 R From cteRN Order By 1) R, cteMM P Where DateAdd(DD,R,D)<=#D2),
cteHH(N,D) as (Select Max(R),Max(DateAdd(HH,R,D)) From (Select Top 24 R From cteRN Order By 1) R, cteDD P Where DateAdd(HH,R,D)<=#D2),
cteMI(N,D) as (Select Max(R),Max(DateAdd(MI,R,D)) From (Select Top 60 R From cteRN Order By 1) R, cteHH P Where DateAdd(MI,R,D)<=#D2),
cteSS(N,D) as (Select Max(R),Max(DateAdd(SS,R,D)) From (Select Top 60 R From cteRN Order By 1) R, cteMI P Where DateAdd(SS,R,D)<=#D2)
Select [Years] = cteYY.N
,[Months] = cteMM.N
,[Days] = cteDD.N
,[Hours] = cteHH.N
,[Minutes] = cteMI.N
,[Seconds] = cteSS.N
--,[Elapsed] = Format(cteYY.N,'0000')+':'+Format(cteMM.N,'00')+':'+Format(cteDD.N,'00')+' '+Format(cteHH.N,'00')+':'+Format(cteMI.N,'00')+':'+Format(cteSS.N,'00')
From cteYY,cteMM,cteDD,cteHH,cteMI,cteSS
)
--Max 1000 years
--Select * from [dbo].[tvf-Date-Elapsed] ('1991-09-12 21:00:00.000',GetDate())
--Select * from [dbo].[tvf-Date-Elapsed] ('2017-01-01 20:30:15','2018-02-05 22:58:35')

I'll leave weeks and leap years to you to figure out unless I have time to come back to this later. However this will get you the years, months, and days you are looking for:
DECLARE #FromDate DateTime
DECLARE #ToDate DateTime
DECLARE #years INT
DECLARE #months INT
DECLARE #days INT
-- just some sample dates for testing
Set #FromDate = '01-01-2014'
Set #ToDate = GetDate()
SET #years = DATEDIFF(mm, #FromDate, #ToDate)/12
SET #months = DATEDIFF(mm, #FromDate, #ToDate)%12 - 1
SET #days = ABS(DATEDIFF(dd, DATEADD(mm,#months , DATEADD(yy, #years, #FromDate)), #ToDate))
DECLARE #YearsStr VarChar(20)
DECLARE #MonthsStr VarChar(20)
DECLARE #DaysStr VarChar(20)
SET #YearsStr = Case When #years > 0 Then Convert(varchar(10),#years) + ' Years, ' Else '' End
SET #MonthsStr = Case When #months > 0 Then Convert(varchar(10),#months) + ' Months, ' Else '' End
SET #DaysStr = Convert(varchar(10),#days) + ' Days '
SELECT #YearsStr + #MonthsStr + #DaysStr

Related

SQL difference between dates(year,month,day)

I have a problem getting difference between two dates in years, months, days without using function, just the Select statement.
So far i have this messy code, but it doesnt work so well, as sometimes the month/day is - . tblProject.BillingDate is StartDate, tblServiceTicket.ServiceTicketDate is EndDate.
CONVERT(varchar(12),datediff(YEAR,tblProject.BillingDate,tblServiceTicket.ServiceTicketDate)) + ' year, '
+ CONVERT(varchar(12),DATEDIFF(MM, DATEADD(YY, datediff(YEAR,tblServiceTicket.ServiceTicketDate,tblProject.BillingDate), tblServiceTicket.ServiceTicketDate), tblProject.BillingDate)) + ' months, '
+ CONVERT(varchar(12),DATEDIFF(DD, DATEADD(MM, DATEDIFF(MM, DATEADD(YY, (datediff(YEAR,tblProject.BillingDate,tblServiceTicket.ServiceTicketDate)), tblProject.BillingDate), tblServiceTicket.ServiceTicketDate), DATEADD(YEAR, datediff(YEAR,tblProject.BillingDate,tblServiceTicket.ServiceTicketDate) , tblProject.BillingDate)), tblServiceTicket.ServiceTicketDate)) + ' days '
The only way that I know to do this is semi-iteratively, like this. This version can be improved with coding around DATEDIFF, but it shows the logic needed more clearly than the DATEDIFF version.
DECLARE #dstart datetime,
#dend datetime,
#dwork datetime
DECLARE #yy int,
#mm int,
#dd int
SET #dstart = '19570125'
SET #dend = '20161214'
DECLARE #ix int
-- Get Year interval
SET #ix = 0
WHILE #ix >= 0
BEGIN
Set #ix = #ix + 1
IF Dateadd(year, #ix, #dstart) > #dend
BEGIN
SET #yy = #ix - 1
SET #ix = -1
END
END
Set #dwork = Dateadd(year, #yy, #dstart)
-- Get month interval
SET #ix = 0
WHILE #ix >= 0
BEGIN
Set #ix = #ix + 1
IF Dateadd(MONTH, #ix, #dwork) > #dend
BEGIN
SET #mm = #ix - 1
SET #ix = -1
END
END
Set #dd = DATEDIFF(day, dateadd(month, #mm, #dwork), #dend)
SELECT 'The difference is ' + Cast(#yy as varchar) + ' years, ' + Cast(#mm as varchar) + ' Months, and ' + Cast(#dd as varchar) + ' Days'
Here are some sample outputs, showing how it handles a couple of problem cases.
-- One day at new years
SET #dstart = '20161231'
SET #dend = '20170101'
-- The difference is 0 years, 0 Months, and 1 Days
-- One month to a shorter month
SET #dstart = '20160131'
SET #dend = '20170228'
-- The difference is 1 years, 1 Months, and 0 Days
-- My age
SET #dstart = '19570125'
SET #dend = '20161214'
-- The difference is 59 years, 10 Months, and 19 Days

Code error in a SQL Server While-If Loop

I know that there are other posts with code that solve my problem but I don't want to take another's code so I'm trying to do it by myself and I'm stuck with the month not increasing problem, so if anyone can help me with that mistake it will be awesome.
The problem is:
I have to populate the table Time from year 1990 to 2016 with all the months and days, I have already achieved that the code works and it populates correctly the years and the days but months increases to January (1) and then is not increasing so the table is filled with all months being January (LOL)
Here's my code:
create table Time
(
Year int,
Month int,
Day int
)
create procedure pTime
as
declare #year int, #month int, #day int;
set #year = 1990;
set #month = 12;
set #day = 10;
while(#year<=2016)
Begin
If(#day = 29)
Begin
set #month = #month + 1;
If(#month = 13)
Begin
set #month = 1;
set #day = 1;
set #year = #year + 1;
insert into Time values (#year, #month, #day);
End
End
else
Begin
If(#day = 29)
Begin
set #month = #month + 1;
set #day = 1;
insert into Time values (#year, #month, #day);
End
Else
Begin
insert into Time values (#year, #month, #day);
set #day = #day + 1;
End
End
End
Any idea where is my mistake or any suggestion?
I didn't look very closely for your mistake because SQL Server has some helpful date arithmetic functions. Here's simplified version of your stored procedure:
create procedure pTime
as
declare #theDate date = '12/10/1990', #days int = 0
while #theDate < '1/1/2016'
begin
insert into Time (Year, Month, Day) values (datepart(year, #theDate), datepart(month, #theDate), datepart(day, #theDate));
set #theDate = dateadd(day, 1, #theDate)
end
Another faster approach would be to use a tally table. Note the code below:
WITH
E(N) AS (SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
SELECT 1 UNION ALL SELECT 1),
iTally(N) AS (SELECT ROW_NUMBER() OVER (ORDER BY (SELECT 1))-1 FROM E a,E b,E c,E d,E e),
dates(dt) AS
(
SELECT TOP(datediff(DAY,'19900101','20160101')) DATEADD(day,N,'19900101')
FROM iTally
)
--INSERT [time] --uncomment for the insert, leave commented to see what will be inserted
SELECT YEAR(dt), MONTH(dt), DAY(dt)
FROM dates;
Why do you need If(#year = 29) condition? In your code this block never will be executed. try this:
create procedure pTime
as
declare #year int, #month int, #day int;
set #year = 1990;
set #month = 12;
set #day = 10;
while(#year<=2016)
Begin
If(#day = 29)
Begin
set #month = #month + 1;
set #day = 1;
If(#month = 13)
Begin
set #month = 1;
set #year = #year + 1;
insert into Time values (#year, #month, #day);
End
End
else
Begin
If(#day = 29)
Begin
set #month = #month + 1;
set #day = 1;
insert into Time values (#year, #month, #day);
End
Else
Begin
insert into Time values (#year, #month, #day);
set #day = #day + 1;
End
End
End
I think first assignment set #day = 1; wasn't in right place. After increasing #month value you should set also #day to 1;

TSQL - converting date string to datetime

I have a table that has the following column (with some sample entries provided):
COLUMN NAME: pt_age_old
20 Years 8 Months 3 Weeks
1 Year 3 Months 2 Weeks
58 Years 7 Months
1 Year
7 Years 11 Months 2 Weeks
26 Years 6 Months
56 Years 6 Months
48 Years 6 Months 4 Weeks
29 Years 11 Months
4 Years 3 Months
61 Years 8 Months 4 Weeks
I have tried to cast it to datetime, of course this did not work. Same with convert.
Keep getting the following message:
Msg 241, Level 16, State 1, Line 2
Conversion failed when converting date and/or time from character string.
Main 2 questions are:
Is this type of conversion even possible with this existing string format?
If so, can you steer me in the right direction so I can make this happen?
Thanks!
This could be done using custom code such as below - I've assumed the values you're using are people's ages, and you're trying to work out their date of birth given their age today.
You can see the below code in action here: http://sqlfiddle.com/#!3/c757c/2
create function dbo.AgeToDOB(#age nvarchar(32))
returns datetime
as
begin
declare #pointer int = 0
, #pointerPrev int = 1
, #y nvarchar(6)
, #m nvarchar(6)
, #w nvarchar(6)
, #d nvarchar(6)
, #result datetime = getutcdate() --set this to the date we're working to/from
--convert various ways of expressing units to a standard
set #age = REPLACE(#age,'Years','Y')
set #age = REPLACE(#age,'Year','Y')
set #age = REPLACE(#age,'Months','M')
set #age = REPLACE(#age,'Month','M')
set #age = REPLACE(#age,'Weeks','W')
set #age = REPLACE(#age,'Week','W')
set #age = REPLACE(#age,'Days','D')
set #age = REPLACE(#age,'Day','D')
set #pointer = isnull(nullif(CHARINDEX('Y',#age),0),#pointer)
set #y = case when #pointer > #pointerprev then SUBSTRING(#age,#pointerprev,#pointer - #pointerprev) else null end
set #pointerPrev = #pointer + 1
set #pointer = isnull(nullif(CHARINDEX('M',#age),0),#pointer)
set #m = case when #pointer > #pointerprev then SUBSTRING(#age,#pointerprev,#pointer - #pointerprev) else null end
set #pointerPrev = #pointer + 1
set #pointer = isnull(nullif(CHARINDEX('W',#age),0),#pointer)
set #w = case when #pointer > #pointerprev then SUBSTRING(#age,#pointerprev,#pointer - #pointerprev) else null end
set #pointerPrev = #pointer + 1
set #pointer = isnull(nullif(CHARINDEX('D',#age),0),#pointer)
set #d = case when #pointer > #pointerprev then SUBSTRING(#age,#pointerprev,#pointer - #pointerprev) else null end
set #result = dateadd(YEAR, 0 - isnull(cast(#y as int),0), #result)
set #result = dateadd(MONTH, 0 - isnull(cast(#m as int),0), #result)
set #result = dateadd(Week, 0 - isnull(cast(#w as int),0), #result)
set #result = dateadd(Day, 0 - isnull(cast(#d as int),0), #result)
return #result
end
go
select dbo.AgeToDOB( '20 Years 8 Months 3 Weeks')
NB: there's a lot of scope for optimisation here; I've left it simple above to help keep it clear what's going on.
Technically it is possible to convert the relative time label into a datetime, but you will need a reference date though (20 Years 8 Months 3 Weeks as of '2013-10-16'). The reference date is most likely today (using GETDATE() or CURRENT_TIMESTAMP), but there is a chance it is a different date. You will have to parse the label, convert it to a duration, and then apply the duration to the reference time. This will be inherently slow.
There are at least two possible ways of doing this, write a FUNCTION to parse and convert the relative time label or use a .NET extended function to do the conversion. You would need to identify all of the possible labels otherwise the conversion will fail. Keep in mind that the reference date is important since 2 months is not a constant number of days (Jan-Feb = either 58 or 59 days).
Here is a sample of what the function might look like:
-- Test data
DECLARE #test varchar(50)
, #ref_date datetime
SET #test = '20 Years 8 Months 3 Weeks'
SET #ref_date = '2013-10-16' -- or use GETDATE() / CURRENT_TIMESTAMP
-- Logic in function
DECLARE #pos int
, #ii int
, #val int
, #label varchar(50)
, #result datetime
SET #pos = 0
SET #result = #ref_date
WHILE (#pos <= LEN(#test))
BEGIN
-- Parse the value first
SET #ii = NULLIF(CHARINDEX(' ', #test, #pos), 0)
SET #label = RTRIM(LTRIM(SUBSTRING(#test, #pos, #ii - #pos)))
SET #val = CAST(#label AS int)
SET #pos = #ii + 1
-- Parse the label next
SET #ii = NULLIF(CHARINDEX(' ', #test, #pos), 0)
IF (#ii IS NULL) SET #ii = LEN(#test) + 1
SET #label = RTRIM(LTRIM(SUBSTRING(#test, #pos, #ii - #pos)))
SET #pos = #ii + 1
-- Apply the value and offset
IF (#label = 'Days') OR (#label = 'Day')
SET #result = DATEADD(dd, -#val, #result)
ELSE IF (#label = 'Weeks') OR (#label = 'Week')
SET #result = DATEADD(ww, -#val, #result)
ELSE IF (#label = 'Months') OR (#label = 'Month')
SET #result = DATEADD(mm, -#val, #result)
ELSE IF (#label = 'Years') OR (#label = 'Year')
SET #result = DATEADD(yy, -#val, #result)
END
SELECT #result
-- So if this works correctly,
-- 20 Years 8 Months 3 Weeks from 2013-10-16 == 1993-01-26
Make a function to split it up:
create function f_tst
(
#t varchar(200)
) returns date
begin
declare #a date = current_timestamp
;with split1 as
(
select 1 start, charindex(' ', #t + ' ', 4)+1 stop
where #t like '% %'
union all
select stop, charindex(' ', #t + ' ', stop + 4)+1
from split1
where charindex(' ', #t, stop) > 0
), split2 as
(
select cast(left(x.sub, charindex(' ', x.sub)) as int) number,
substring(x.sub, charindex(' ', x.sub) + 1, 1) unit
from split1
cross apply (select substring(#t, start, stop - start) sub) x
)
select #a = case unit when 'W' then dateadd(ww, -number, #a)
when 'Y' then dateadd(yy, -number, #a)
when 'M' then dateadd(mm, -number, #a)
end
from split2
return #a
end
Test function:
select dbo.f_tst(age)
from (values('20 Years 8 Months 3 Weeks'),('1 Month') ) x(age)
Result:
1993-01-27
2013-09-17
No, date time type presents actual date, like YYYY-MM-DD HH:MM, your strings are not DATE fields, they are age, to have date time you need date of birth and them somehow add this age to it

SQL query to find out experience of employee in the format 'yy years mm months dd days'

I want a query to find out experience of employee in the format 'yy years mm months dd days'.
SELECT EMPID, EMPNAME, DEPARTMENT, DESIGNATION, DATEDIFF(YEAR, DOJ, GETDATE()) AS EXPERIENCE,
EMPSTATUS AS JOB_STATUS
FROM EMPLOYEE
DOJ - field in db for saving 'date of joining' of employee.
This is the query which returns experience in years only. How to modify it?
SELECT
EMPID, EMPNAME, DEPARTMENT, DESIGNATION,
convert(varchar(3),DATEDIFF(MONTH, DOJ, GETDATE())/12) +' years '+
convert(varchar(2),DATEDIFF(MONTH, DOJ, GETDATE()) % 12)+ ' months'
AS EXPERIENCE,
EMPSTATUS AS JOB_STATUS
FROM EMPLOYEE
Consider Emp_joiningDate as column
SELECT DATEDIFF(year, Emp_joiningDate, GETDATE()) AS Years,
DATEDIFF(month, Emp_joiningDate, GETDATE()) - DATEDIFF(year, '1/2/1999', GETDATE()) * 12 AS Months,
DATEDIFF(day, Emp_joiningDate, getdate())- DATEDIFF(month, '1/2/1999',
GETDATE()) - DATEDIFF(year, '1/2/1999', GETDATE()) * 365 as Days
SQL Fiddle for testing
SELECT EMPID, EMPNAME, DEPARTMENT, DESIGNATION,
cast(floor(experience / 365) as varchar) + ' years ' +
cast(floor(experience % 365 / 30) as varchar) + ' months ' +
cast(experience % 30 as varchar) + ' days' as experience,
EMPSTATUS AS JOB_STATUS
FROM (select *, datediff(DAY, doj, getdate()) as experience
from employee) t
DECLARE #FromDate DATETIME = '2013-12-01 23:59:59.000',
#ToDate DATETIME = '2016-08-30 00:00:00.000',
DECLARE #MONTHS INT
SET #Months = DATEDIFF(MM,#FROMDATE,#TODATE)
IF (DATEPART(MM,#FromDate) <= (DATEPART(MM,#TODATE))+1)
BEGIN
SELECT CAST(DATEDIFF(YY,#FROMDATE,#TODATE) AS VARCHAR(5)) + ' Years '+ CAST( (DATEPART(MM,#TODATE)-DATEPART(MM,#FROMDATE)) AS VARCHAR(5))+1 +' Month'
END
ELSE
BEGIN
SELECT CAST(DATEDIFF(YY,#FROMDATE,#TODATE)-1 AS VARCHAR(5)) +' Years '+ CAST(12-(DATEPART(MM,#FROMDATE)) + DATEPART(MM,#TODATE)+1 AS VARCHAR(5) ) +' Month'
END
create FUNCTION [dbo].[fn_getEmployePeriod]
(
#dateofbirth DATETIME
)
RETURNS VARCHAR(100)
AS
BEGIN
DECLARE #currentdatetime DATETIME;
DECLARE #years INT;
DECLARE #months INT;
DECLARE #days INT;
DECLARE #currentMonthdays INT;
DECLARE #result VARCHAR(100);
SET #currentdatetime = GETDATE();--current datetime
IF ( #dateofbirth <= GETDATE() )
BEGIN
SELECT #years = DATEDIFF(YEAR, #dateofbirth, #currentdatetime); -- To find Years
SELECT #months = DATEDIFF(MONTH, #dateofbirth,
#currentdatetime) - ( DATEDIFF(YEAR,
#dateofbirth,
#currentdatetime)
* 12 );
SELECT #days = DATEPART(d, #currentdatetime) - DATEPART(d,
#dateofbirth);-- To Find Days
SELECT #currentMonthdays = ( SELECT DAY(DATEADD(DD, -1,
DATEADD(mm,
DATEDIFF(mm, 0,
GETDATE()), 0)))
);
IF ( #months < 0 )
BEGIN
SET #months = 12 + #months;
SET #years = #years - 1;
END;
IF ( #days < 0 )
BEGIN
SET #days = #currentMonthdays + #days;
IF(#months<>0)
begin
SET #months = #months - 1;
END
ELSE
BEGIN
SET #years = #years - 1;
SET #months = 11;
end
END;
-- To Find Months
SET #result = CAST(#years AS VARCHAR(3)) + ' years, '
+ CAST(#months AS VARCHAR(3)) + ' months, '
+ CAST(#days AS VARCHAR(3)) + ' days';
END;
ELSE
BEGIN
SET #result = 'Invaild date of birth';
END;
RETURN #result;
END;
SELECT convert(varchar(3),DATEDIFF(MONTH, '2015-01-01', GETDATE())/12) +' years '+
convert(varchar(2),DATEDIFF(DD, '2016-09-21', GETDATE()) % 12)+ ' months'
AS EXPERIENCE

Display Employee Name, Dept Name, Salary, Grade, Experience (EX: XX Years YY Months ZZ Days) for all the employees in SQL

please help in the following query
Display Employee Name, Dept Name, Salary, Grade, Experience (EX: XX Years YY Months ZZ Days) for all the employees
ENAME DNAME SAL GRADE EXPERIENCE
SCOTT RESEARCH 3000 4 12 Years 3 months 15 days
like this i need to get the output.
i have tried to write upto years but months,days i am not able to get.
select distinct ename,dname,sal,grade,
(round((months_between(sysdate,hiredate)/12))||' years' EXP
from emp,salgrade,dept
where dept.deptno=emp.deptno and sal between losal and highsal;
You have got the years.
Use MONTHS_BETWEEN(date1, date2) to get months. Then subtract (year * 12).
Use DAYS_BETWEEN(date1, date2) to get number of days.
See this for more details
Here's a T-SQL (SQL Server) solution which I've got working (based on tests so far). Sadly I don't have an oracle instance to play on, but hopefully this shouldn't be too hard to convert:
declare #fromDate date = '2010-11-21'
, #toDate date = getutcdate()
--from date must be before to date
declare #tempDate date
if #toDate < #fromDate
begin
set #tempDate = #toDate
set #toDate = #fromDate
set #fromDate = #tempDate
end
declare #fDD int = datepart(dd,#fromdate)
, #tDD int = datepart(dd,#todate)
, #fMM int = datepart(mm,#fromdate)
, #tMM int = datepart(mm,#todate)
, #fYYYY int = datepart(yyyy,#fromdate)
, #tYYYY int = datepart(yyyy,#todate)
, #y int, #m int, #d int
--calc difference in years
set #y = #tYYYY-#fyyyy
if #fMM > #tMM or (#fMM=#tMM and #fDD > #tDD)
begin
set #y = #y - 1
set #fYYYY = #fYYYY + #y
set #tempDate = DATEADD(year,#y,#fromDate)
end
--calc remaining difference in months
set #m = DATEDIFF(month,#tempDate,#toDate)
if #tDD < #fDD
begin
set #m = #m-1
set #tempDate = DATEADD(month,#m,#tempDate)
end
--calc remaining difference in days
set #d = DATEDIFF(day,#tempDate,#toDate)
--display results in user friendly and grammatically correct way
select cast(#y as nvarchar(3)) + N' year' + case when #y = 1 then N'' else N's' end + N' '
+ cast(#m as nvarchar(2)) + N' month' + case when #m = 1 then N'' else N's' end + N' '
+ cast(#d as nvarchar(3)) + N' day' + case when #d = 1 then N'' else N's' end + N' '
Here is your required answer in ORACLE SQL
select (floor(months_between(sysdate,hiredate)/12)) YEARS,
round(((months_between(sysdate,hiredate)/12)-(round(months_between(sysdate,hiredate)/12)))*12) months
from abc;