问题
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;
回答1:
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')
回答2:
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
来源:https://stackoverflow.com/questions/49903501/sql-server-determining-the-years-months-weeks-and-days-between-two-dates