This question already has an answer here:
Sum of hour and minutes in SQL Server 2008
(1 answer)
Closed 8 years ago.
I have one query,but it not giving correct answer.I want to display like this 5 Hour 58 minutes + 7 Hour 58 minutes=13 hour 56 minutes (the answer should be 13.56),but this below query giving output 13.6 .
Please reply me with any other solution. (Effort1 is 5 Hour column, Effort1Minutes is 58 minute column and same Effort2 is 7 Hour column, Effort2Minutes is 58 minute column .
select SUM(Effort1 + Effort2) + SUM(Effort1Minutes + Effort2Minutes) / 60 + CONVERT(decimal(18,2), (SUM(Effort1Minutes + Effort2Minutes) % 60) / 100.00)
from TimesheetDetails
When you have an hammer, every problem looks like a nail. This is a common pitfall, which basically means "use the right tool for each given problem".
Why are you using such a convoluted query to calculate a sum of times?
Why don't you have two TIME columns, which you can later add using ADDDATE(d1, d2)
Why don't you use a TIME INTERVAL datatype, as defined in the SQL 99 Standard?
Why don't you return the result in minutes, and leave the application to convert it into the format of your choosing?
Why don't you return separately the hours and the minutes, and once again leave the application to handle the extra hour that may arise from the sum of the minutes?
As you can see, there are plenty of other solutions that solve your issue from the root, while also granting you a better database design altogether. Please consider them, expecially the number one.
Related
I need random interval time between 0 and (10 days and 5 hours).
My code:
select random() * (interval '10 days 5 hours')
from generate_series(1, 50)
It works like should, except a few strange results, like:
0 years 0 mons 7 days 26 hours 10 mins 1.353353 secs
The problem is 26 hours, it shouldn't be more than 23. And I never get 10 days, what I'd like to.
Intervals in Postgres are quite flexible, so hour values of greater than 23 do not necessarily roll over to days. Use jusify_interval() to return them to the normal "days" and "hours"."
So:
select justify_interval(random() * interval '10 day 5 hour')
from generate_series(1, 200)
order by 1 desc;
will return values with appropriate values for days, hours, minutes, and seconds.
Now, why aren't you getting intervals with more than 10 days? This is simple randomness. If you increase the number of rows to 200 (as above), you'll see them (in all likelihood). If you run the code multiple times, sometimes you'll see none in that range; sometimes you'll see two.
Why? You are asking how often you get a value of 240+ in a range of 245. Those top 5 hours account for 0.02% of the range (about 1/50). In other words a sample of 50 is not big enough -- any given sample of 50 random values is likely to be missing 1 or more 5 hour ranges.
Plus, without justify_interval(), you are likely to miss those anyway because they may show up as 9 days with an hours component larger than 23.
Try this:
select justify_hours(random() * (interval '245 hours'))
FROM generate_series(1, 50)
See Postgres Documentation for an explanation of the justify_* functions.
One option would be to use an interval of one hour, and then multiply by the random number between 0 and 1 coming from the series:
select random() * 245 * interval '1 hour'
from generate_series(1, 50);
I can see that the other answers suggest using justify_interval. If you just want a series of intervals between 0 and 245 hours (245 hours corresponding to 10 days and 5 hours), then my answer should suffice.
I've come across some unexpected behavior of my SQL code in PostgreSQL 9.6 cornering date calculations. While analyzing, I've found out that Postgres will not always return the same value for going back n days and then calculate the duration with age().
Consider this code: We set some day as "base", go 45 days back and then calculate the duration.
WITH basedate AS (SELECT '2018-05-01'::date AS b),
myperiod AS (SELECT (basedate.b - interval '45 days') AS "startDate",
basedate.b AS "endDate" FROM basedate)
SELECT age("endDate","startDate") FROM myperiod;
I expect this query to always result in 45 days. However, if I change my basedate to 2018-06-01, I'll get 44 days.
Why is this the case?
My guess is this is somehow related to May having 31 days. However, I cannot exactely explain why, since both 2018-05-01 and 2018-06-01 will result in the same duration if I change 45 days to 15 days.
That's not quite true.
Your first query returns
age
---------------
1 mon 15 days
(1 row)
and if you modify the date to 2018-06-01, you get
age
---------------
1 mon 14 days
(1 row)
Both are correct, aren't they?
The problem is rather that PostgreSQL treats a month as 30 days in functions like justify_interval:
SELECT justify_interval('45 days');
justify_interval
------------------
1 mon 15 days
(1 row)
But then, the alternative would be to throw an error, and the behavior is clearly documented.
Laurenz Albe's answer points to the right thing: I should have expected a result starting with 1 mon.
The problem was actually in the client, in this case OmniDB 2.8. See here: GitHub OmniDB: Does OmniDB auto-convert intervals to days? The issue was fixed in OmniDB 2.9
In all other clients I've tested I got a result like Laurenz Albe predicted.
So, if you come across here while having a similar issue: By any means, check if the problem is in your client or the library you use.
So in my query, I simply need to narrow down the results by whoever's status was last updated 3 weeks ago.
The line:
AND DATE_ADD(user_status_updated, INTERVAL - 21 DAY)
returns the results as an interval from now and 21 days from now. I am only interested in the exact date 21 days ago, but cannot find the right function to use.
I am writing automatic emails and part of this project is to check daily for users that have been inactive for 3 weeks, so it will always be a matter of NOW() minus 21 days. But I don't want the interval, I want the exact 21 days ago since last updated results.
Ok I found my solution.
WHERE user_status_updated= DATE_SUB(DATE(NOW()), INTERVAL 21 DAY)
The user_status_updated is a DATETIME field
I would like to subtract 1 date from another and get the days hours and mins in-between.
I know there is a DateDiff function, however it does not work with all 3 time values; days hours and mins. I would like this doable in an SQL statement. Currently I have the following.
SELECT id, pickupdateandtime, GETDATE() AS CurrentTime,
(DATEDIFF(day,GETDATE(),pickupdateandtime)) AS Days,
(DATEDIFF(hour,GETDATE(),pickupdateandtime)) AS Hours,
(DATEDIFF(minute,GETDATE(),pickupdateandtime)) AS Mins FROM orders
And it shows up like this:
If we can stick it all in 1 column that's fine too.
I agree with #AndyMcLaughlin about the use of the mod operator % here. It's very handy for this sort of thing. However, I have a general distrust of DATEDIFF. That function does not count the whole number of years (say) between two dates, but the number of year boundaries between them.
So DATEDIFF "thinks" the difference in years between 01-Jan-2000 and 01-Jan-2001 is the same as that between 31-Dec-2000 and 01-Jan-2001.
This is why #Michael saw a need to subtract 1 from #AndyMcLaughlin's results. Unfortunately, that doesn't always work, it will depend on the individual case.
As a rule, DATEDIFF works well when it's used against the smallest interval you are interested in. So if you are interested in years and simply want to separate one calendar year from another, it'll serve you well.
I think the smallest interval we are interested in here is minutes. So we can use DATEDIFF for that, but have to work upwards from there to hours and days:
select
mf.id,
mf.pickupdateandtime,
mf.CurrentTime,
--The divisions in the following lines simply
--truncate since all the numbers are integers
--but that works in our favour here
(mf.MinutesFull/(60*24)) as Days,
(mf.MinutesFull/60) % 24 as Hours,
mf.MinutesFull % 60 as Minutes
from
(
select
id,
pickupdateandtime,
getdate() as CurrentTime,
datediff(minute, getdate(), pickupdateandtime) as MinutesFull
from #orders
) mf
You need to use the mod operator % to remove whole days from hours and whole hours from minutes.
So you can do something like:
SELECT
id,
pickupdateandtime,
GETDATE() AS CurrentTime,
(DATEDIFF(day,GETDATE(),pickupdateandtime)) AS Days,
(DATEDIFF(hour,GETDATE(),pickupdateandtime) % 24) AS Hours,
(DATEDIFF(minute,GETDATE(),pickupdateandtime) % 60) AS Mins FROM orders
I am writing a little query in SQL and am butting heads with an issue that it seems like someone must have run into before. I am trying to find the number of months between two dates. I am using an expression like ...
DATEDIFF(m,{firstdate},{seconddate})
However I notice that this function is tallying the times the date crosses the monthly threshold. In example...
DATEDIFF(m,3/31/2011,4/1/2011) will yield 1
DATEDIFF(m,4/1/2011,4/30/2011) will yield 0
DATEDIFF(m,3/1/2011,4/30/2011) will yield 1
Does anyone know how to find the months between two dates more-so based upon time passed then times passed the monthly threshold?
If you want to find some notional number of months, why not find the difference in days, then divide by 30 (cast to FLOAT as required). Or 30.5-ish perhaps - depends on how you want to handle the variable month length throughout the year. But perhaps that's not a factor in your particular case.
The following statements have the same startdate and the same endate. Those dates are adjacent and differ in time by .0000001 second. The difference between the startdate and endate in each statement crosses one calendar or time boundary of its datepart. Each statement returns 1. ...
SELECT DATEDIFF(month, '2005-12-31 23:59:59.9999999'
, '2006-01-01 00:00:00.0000000'); ....
(from DATEDIFF, section datepart Boundaries ). If you are not satisfied by it, you probably need to use days as unit as proposed by martin clayton
DATEDIFF(m,{firstdate},ISNULL({seconddate},GETDATE())) - CASE
WHEN DATEPART(d,{firstdate}) >= DATEPART(d,ISNULL({seconddate},GETDATE()))
THEN 1
ELSE 0
DATEDIFF is like this by design. When evaluating a particular time measurement (like months, or days, etc.), it considers only that measurement and higher values -- ignoring smaller ones. You'll run into this behavior with any time measurement. For example, if you used DATEDIFF to calculate days, and had one date a few seconds before midnight, and another date a few seconds after midnight, you'd get a "1" day difference, even though the two dates were only a few seconds apart.
DATEDIFF is meant to give a rough answer to questions, like this:
Question: how many years old are you?
Answer: some integer. You don't say "I'm 59 years, 4 months, 17 days, 5 hours, 35 minutes and 27 seconds old". You just say "I'm 59 years old". That's DATEDIFF's approach too.
If you want an answer that's tailored to some contextual meaning (like your son who says "I'm not 8! I'm 8 and 3-quarters!, or I'm almost 9!), then you should look at the next-smallest measurement and approximate with it. So if it's months you're after, then do a DATEDIFF on days or hours instead, and try to approximate months however it seems most relevant to your situation (maybe you want answers like 1-1/2 months, or 1.2 months, etc.) using CASE / IF-THEN kinds of logic.