Subtracting two dates using PostgreSQL - sql

I am trying to subtract 2 dates from each other but it seems that it is not subtracting properly and i am not sure what i am doing wrong here. I am using case statement to flag as 1 if the difference between the dates are less than 90 days else flag it as 0. But it is always flagging as 1 even if the difference between the dates are greater than 90 days. I am PostgreSQL here and here is my case statement:
CASE WHEN EXTRACT(DAY FROM CAST(SVS_DT AS DATE) - CAST(DSCH_TS AS DATE)) <90
THEN 1 ELSE 0 END AS FU90
example of the dates are here:
SVS_DT DSCH_TS
2013-03-22 00:00:00 2010-05-06 00:00:00
it is suppose to flag as 0 in this case but it is flagging as 1 because the difference between these 2 dates are greater than 90 days.

extract of a day returns the day element of a date. Since days are always between 1 and 31, the maximum difference is 30, and cannot be larger than 90.
Subtracting dates returns the difference in days, as an integer. So you need only drop the extract calls:
CASE WHEN (CAST(SVS_DT AS DATE) - CAST(DSCH_TS AS DATE)) < 90 THEN 1
ELSE 0
END AS FU90

you can use below one:
CASE WHEN (EXTRACT(EPOCH FROM (DATE_COLUMN_2 - DATE_COLUMN_1)) < (90*24*60*60)
THEN 1 ELSE 0 END AS FU90
here it returns the deference in seconds so you need to convert 90 days into seconds

A slightly shorter CAST version.
SELECT CASE WHEN SVS_DT::DATE - DSCH_TS::DATE < 90
THEN 1
ELSE 0
END
AS FU90

We can also do it without using any EXTRACT or CAST keyword like:
SELECT CASE WHEN (DATE(SVS_DT)- DATE(DSCH_TS)) < 90
THEN 1
ELSE 0
END AS FU90

Related

How to get the exact year difference in SQL

I need a simple way to find the exact year difference between two dates.
For example between 01.11.2013 and 30.10.2019. In this case, it should be 5 years because current date has not reached 01.11 yet. But if it is between 29.10.2013 and 30.10.2019 then it should be 6 years.
Let assume these are birthday and current days. It's similar.
I've tried to get the result with the following code:
select datediff(year,20131101,20191030)
It is giving me 6 instead of 5. The closest solution I've found is:
select datediff(day,20131101,20191030)/365
But as we know there could be 1 or 2 leap years in this period and the result is again not exactly what I expect. Neither when we divide with 365 nor 366.
It is not necessary using datediff. I wonder if there is some simple way to present it or some stored function to do this for which existing I did't know.
You can take the year difference and then subtract 1 if necessary:
select (datediff(year, val1, val2) -
(case when month(val1) < month(val2) then 0
when month(val1) > month(val2) then 1
when day(val1) <= day(val2) then 0
else 1
end)
) as diff_years
You can try datediff Month / 12.
Declare #date1 Datetime
Declare #date2 Datetime
set #date1='2013-11-01 23:59:59'
set #date2='2019-10-30 00:00:00'
(select (datediff(HOUR, #date1, #date2)/24/365 ) as diff_years)

did not get perfect answer with datediff

Datediff is very confusing for fetch the difference between two days:
DATEDIFF(DAY, '2014-09-01','2014-09-07')+1) AS totaldays
when i write above then it will give me 7 days it is perfect....
but when i write the below
DATEDIFF(DAY, '2014-09-01','2014-09-02')+1) AS totaldays
then it will give me 2 days but i want 1 day with this below function
DATEDIFF(DAY, '2014-09-01','2014-09-02')+1) AS totaldays
how can i get it?
The DATEDIFF function is working as expected.
DATEDIFF simply subtracts the second parameter from the third parameter by the specific element specified in the first parameter.
For example, the following query provides four columns... all which subtract the day specified in the second parameter from the day specified in the third:
Select DATEDIFF(DAY, '2014-09-01','2014-09-07')+1, -- Calculation: (7-1)+1 = 7
DATEDIFF(DAY, '2014-09-01','2014-09-07'), -- Calculation: (7-1) = 6
DATEDIFF(DAY, '2014-09-01','2014-09-02')+1, -- Calculation: (2-1)+1 = 2
DATEDIFF(DAY, '2014-09-01','2014-09-02') -- Calculation: (2-1) = 1
And the results are as expected:
Col1 | Col2 | Col3 | Col4
7 | 6 | 2 | 1
Here is the MS documentation for this function.
If you want the function to act differently then you will need to utilize CASE statement.
A "day" has a "duration" (that can be measured in units such as 24 hours)
That duration commences at 00:00:00 +0000
When we write a date/time as '2014-09-07' the time is assumed to be
00:00:00 +0000
so this: DATEDIFF(DAY, '2014-09-01','2014-09-07')
is measuring a duration between: "the start of 2014-09-01" to "the start of 2014-09-07" and; trying to represent this as a set of durations where "S" indicates the start point and "---" is the countable duration.
1 2 3 4 5 6 7 (day of the month)
S---S---S---S---S---S---S (time span)
Count the number of "---" in that time span (6) [and the number of start points, "S", is 7]
DATEDIFF() is measuring "duration" NOT the number of start points
What you need to do is add one day to the higher date instead of adding one to the result, because you are using "start of" not "end of" points of time.
from to
start of start of
DATEDIFF(DAY, '2014-09-01','2014-09-08') = 7
DATEDIFF(DAY, '2014-09-01','2014-09-07') = 6
DATEDIFF(DAY, '2014-09-01','2014-09-06') = 5
DATEDIFF(DAY, '2014-09-01','2014-09-05') = 4
DATEDIFF(DAY, '2014-09-01','2014-09-04') = 3
DATEDIFF(DAY, '2014-09-01','2014-09-03') = 2
DATEDIFF(DAY, '2014-09-01','2014-09-02') = 1
DATEDIFF(DAY, '2014-09-01','2014-09-01') = 0
Remove +1 from your SQL.
Use the following : DATEDIFF(DAY, '2014-09-01','2014-09-02') AS totaldays
Due to +1 it was giving wrong output.

How to get every Monday out of a Date

I need some help to understand a certain line in a code. The code takes the turnovers of every Monday in the year 2010 and at the last line in summarizes all the turnovers to one.
Here is the code:
SELECT
CASE
WHEN GROUPING (DATUM) = 1 THEN 'Gesamtumsatz'
ELSE CAST (DATUM AS VARCHAR (40))
END AS MONTAGSDATEN,
AVG (VERKAUFSWERT * VERKAUFSMENGE) as UMSATZ
FROM Data_Star_Awesome.dbo.VERKAUFSFAKTEN vk
INNER JOIN DIMDATUM dimD on vk.DATUMID=dimD.DATUMID
WHERE DATEDIFF(dd,0, DATUM)%7=0
AND JAHR = 2010
GROUP BY ROLLUP (DATUM)
The problematic line I don't understand is the following:
WHERE DATEDIFF(dd,0, DATUM)%7=0
What I know is that it takes the days out of the date variable but I don't get the %7=0 part. The DATEDIFF function should give back all the days. Are these days saved in the % the placeholder? And how does it get all the Mondays by using the 7=0?
It would be great if someone could help me out.
Thanks a lot :)
Modulo or % operator is the same as in a lot of programming languages. It returns the remainder after the division.
The DATEDIFF function takes two dates and returns the difference in a specified datepart, which in your query is the days represented by dd.
Also 0 as date converts to 1/1/1900 which happens to be a Monday. So your query is calculating the days between 1/1/1900 and the DATUM field in days and if its Mod is 0 then DATUM is Monday.
You could simply say:
datename(weekday,<datetime-value>)
Which will return 'Monday', 'Tuesday', 'Wednesday', etc. The problem with this approach is that the returned value is localized. If the SQL server's language is changed, your test for 'Monday' will fail.
This expression will always work:
( ##datefirst + ( datepart(weekday,today) - 1 ) ) % 7
It evaluates to 1-7, where Monday is always 1 and Sunday is always 7, regardless of culture/language settings or the current value of ##datefirst, as set by set datefirst.
We can then convert this into this discriminant function, yielding 1 or 0 to indicate whether or not the date is Monday:
case ( ##datefirst + ( datepart(weekday,today) - 1 ) ) % 7 when 1 then 1 else 0 end

Datediff, how to not include start date that's in the future

My current query is
case when datediff(day, isnull(installment_duedate, due_date), getdate()) < 30
then amount_due else 0 end as [unpaid under 30 days]
I have another 3 case that does between 30-60 days, between 60-90s and greater then 90 days.
My current problem is, my installment_duedate has future dates in it. If someone has an installment due date it would be something like 12-02-2012, 03-02-2013, 06-02-2013, 09-02-2013. If it's in the future I would like it to return as 0 for now but currently the amount is showing up in my under 30 days.
Any pointers would be greatly appreciated!
You need to change your case to:
CASE WHEN DATEDIFF(DAY, ISNULL(installment_duedate, due_date), GETDATE()) BETWEEN 0 AND 30
THEN amount_due ELSE 0 END AS [unpaid under 30 days]
In this line , when installment_duedate is past date , DATEDIFF returns negative value which is less than 30 and hence < 30 condition gets satisfied
datediff(day, isnull(installment_duedate, due_date), getdate()) < 30
Format
DATEDIFF(datepart,startdate,enddate)
for solution to it , see Lamak's answer below

how to get the number on months between two dates in sql server 2005

I have a column in my sql server 2005 table that should hold the number of months an employee has been in service.
Since I also have the date the employee was engaged, I want the "months_In_Service" column to be a computed column.
Now if I use DATEDIFF(month,[DateEngaged],GETDATE()) as the formula for the months in service computed column, the results are correct some times and other times incorrect.
What would be the better reliable way to get the number of months between the DateEngaged value and the current date? Which formula should i use in my computed column?
Something like (might need to swap the 1 and 0, untested)
datediff(month,[DateEngaged],getdate()) +
CASE WHEN DATEPART(day, [DateEngaged]) < DATEPART(day, getdate()) THEN 1 ELSE 0 END
DATEDIFF measure month boundaries eg 00:00 time on 1st of each month, not day-of-month anniversaries
Edit: after seeing OP's comment, you have to subtract 1 if the start day > end day
DATEDIFF (month, DateEngaged, getdate()) -
CASE
WHEN DATEPART(day, DateEngaged) > DATEPART(day, getdate()) THEN 1 ELSE 0
END
So for 20 Dec to 13 Jan, DATEDIFF gives 1 and then 20 > 13 so subtract 1 = zero months.
Same approach as gbn, but with less keystrokes :-)
SELECT
DATEDIFF(MONTH, DateEngaged, GETDATE()) +
CASE
WHEN DAY(DateEngaged) < DAY(GETDATE())
THEN 1
ELSE 0
END
Maybe you want something like:
(year(getdate())-year([DateEngaged]))*12+(month(getdate())-month([DateEngaged]))
If You presume that month is meaning for 30 days You can also round vale
round((datediff(day,[DateEngaged],getdate()))/30.00,0)