SQL Subtract Hours - sql

In SQL I have one column A (float) which contains 2173.03 value. You can read this as 2173 Hours and 3 minutes.
I have another column-(B) in float which contains 2171.33 value. Same way you can read this as 2171 Hours and 33 Minutes.
In SQL I want to subtract Column B From A.
So what I need is to convert Column A's 2173 in Minutes and then want to add 3 minutes to the result.
Column A: 2173 * 60 + 03 = 1,30,383 Minutes
Column B: 2171 * 60 + 33 = 1,30,293 Minutes
Column A-B = 90 Minutes
I want the result as 1.30. How can I achieve this?

Why are you using such an arcane format? Why not just decimal minutes, where the decimals mean what decimals mean?
You can convert to decimal minutes by doing:
select (floor(cola) - (cola - floor(cola)) * 100.0 / 60)
Then do the subtraction:
select ( (floor(cola) - (cola - floor(cola)) * 100.0 / 60) -
(floor(colb) - (colb - floor(colb)) * 100.0 / 60)
)
And then to convert back, I would use a subquery:
select (floor(decimal_minutes) +
(decimal_minutes - floor(decimal_minutes) * 60.0 / 100)
)
from (select ( (floor(cola) - (cola - floor(cola)) * 100.0 / 60) -
(floor(colb) - (colb - floor(colb)) * 100.0 / 60)
) as decimal_minutes
) x
However, I would strongly advise you to change the meaning of the column to either decimal minutes or just seconds.

Related

Converting an nvarchar into a custom time format to query upon duration

I am working with a table that has a variety of column types in rather specific and strange formats. Specifically I have a column, 'Total_Time' that measures a duration in the format:
days:hours:minutes (d:hh:mm)
e.g 200:10:03 represents 200 days, 10 hours and 3 minutes.
I want to be able to run queries against this duration in order to filter upon time durations such as
SELECT * FROM [TestDB].[dbo].[myData] WHERE Total_Time < 0:1:20
Ideally this would provide me with a list of entries whose total time duration is less than 1 hour and 20 minutes. I'm not aware of how this is possible in an nvarchar format so I would appreciate any advice on how to approach this problem. Thanks in advance...
I would suggest converting that value to minutes, and then passing the parametrised value as minutes as well.
If we can assume that there will always be a days, hours, and minutes section (so N'0:0:10' would be used to represent 10 minutes) you could do something like this:
SELECT *
FROM (VALUES(N'200:10:03'))V(Duration)
CROSS APPLY (VALUES(CHARINDEX(':',V.Duration)))H(CI)
CROSS APPLY (VALUES(CHARINDEX(':',V.Duration,H.CI+1)))M(CI)
CROSS APPLY (VALUES(TRY_CONVERT(int,LEFT(V.Duration,H.CI-1)),TRY_CONVERT(int,SUBSTRING(V.Duration,H.CI+1, M.CI - H.CI-1)),TRY_CONVERT(int, STUFF(V.Duration,1,M.CI,''))))DHM(Days,Hours,Minutes)
CROSS APPLY (VALUES((DHM.Days*60*24) + (DHM.Hours * 60) + DHM.Minutes))D(Minutes)
WHERE D.[Minutes] < 80; --1 hour 20 minutes = 80 minutes
If you can, then ideally you should be fixing your design and just storing the value as a consumable value (like an int representing the number of minutes), or at least adding a computed column (likely PERSISTED and indexed appropriately) so that you can just reference that.
If you're on SQL Server 2022+, you could do something like this, which is less "awful" to look at:
SELECT *
FROM (VALUES(N'200:10:03'))V(Duration)
CROSS APPLY(SELECT SUM(CASE SS.ordinal WHEN 1 THEN TRY_CONVERT(int,SS.[value]) * 60 * 24
WHEN 2 THEN TRY_CONVERT(int,SS.[value]) * 60
WHEN 3 THEN TRY_CONVERT(int,SS.[value])
END) AS Minutes
FROM STRING_SPLIT(V.Duration,':',1) SS)D
WHERE D.[Minutes] < 80; --1 hour 20 minutes = 80 minutes;

Query using group by with steps/range over large data

I have a table that stores a sensor temperature readings every few seconds
Sample data looks like this
nId nOperationId strDeviceIp nIfIndex nValue nTimestamp
97 2 192.168.99.252 1 26502328 1593828551
158 2 192.168.99.252 1 26501704 1593828667
256 2 192.168.99.252 1 26501860 1593828788
354 2 192.168.99.250 1 26501704 1593828908
452 2 192.168.99.250 1 26501692 1593829029
I want to have the average temperature per device so I ran the following query
select strDeviceIp, AVG(CAST(nValue as bigint)) as val1
from myTable
where nOperationId = 2 and nTimestamp >= 1593828600 and nTimestamp <= 1593838600
group by strSwitchIp;
Where I can pass the time range I want.
My issue is that this gives me total average but I want steps or range
I want to achieve that instead of one line I'll get all the values in a range/step of say 5 minutes as a row.
P.S. I'm trying to show a graph.
Running the following query I get
strSwitchIp average
192.168.99.252 26501731
But I would like to get
strSwitchIp average timestamp
192.168.99.252 26201731 1593828600
192.168.99.252 26532731 1593828900
192.168.99.252 24501721 1593829200
192.168.99.252 26506531 1593829500
In this example I would like to get a row every 300 seconds, per device.
Since your nTimestamp is number of seconds, you can simply add it to the GROUP BY. Division by 300 gives you 300 second (5 minute) intervals. In SQL Server / is integer division, which discards the fractional part.
select
strSwitchIp
,AVG(CAST(nValue as bigint)) as val1
,(nTimestamp / 300) * 300 AS Timestamp
from myTable
where
nOperationId = 2 and nTimestamp >= 1593828600 and nTimestamp <= 1593838600
group by
strSwitchIp
,nTimestamp / 300
;
nTimestamp / 300 gives an integer, a number of 5-minute intervals since 1970. / discards here the fractional part.
When this number is multiplied back by 300, it becomes again the number of seconds since 1970, but "rounded" to the nearest 5-minute interval. Just as you showed in the question in the expected result.
For example:
1593828667 / 300 = 5312762.2233333333333333333333333
discard fractional part
1593828667 / 300 = 5312762
5312762 * 300 = 1593828600
So, all timestamps between 1593828600 and 1593828899 become 1593828600 and all values for these timestamps are grouped into one row and averaged.
you ca use partition like this:
select strDeviceIp, AVG(CAST(nValue as bigint)) as val1,
ROW_NUMBER() over(partition by nTimestamp order by nTimestamp desc) as ROW_NO from AmyTable) Q where q.ROW_NO%5=0
....

hi all i have a logic being implemented, my requirement is that the sum of the column should not exceed 24hrs

Ex:-Time(in minutes)
12.38474
88.47683
8.7363
0.19873
what ever the sum of the column is it should not exceed 24hrs.
Please consider the above column 'time' which has data in minutes. If the sum of all the entities is <24hrs then thats fine, however if the sum is >24hrs then the values should be capped to 24hrs example Total Time<24hrs then thats fine
however if total time is 24.02 or 24.10 then value should be 24hrs only
could you please help in enhancing the approach
select round(cast( (case when (cast(66.3763731333299 as decimal) % 1000) <> 0
then cast((cast((66.3763731333299 / 1000) as int) * 1000) as int) + 1000
else 66.3763731333299
end)
as float) / 60000, 6)
Please consider the above column 'time' which has data in minutes. If the sum of all the entities is <24hrs then thats fine, however if the sum is >24hrs then the values should be capped to 24hrs example Total Time<24hrs then thats fine 24.02 or 24.10 then value should be 24hrs only
Do you simply want least()?
select least(sum(minutes), 24 * 60)
from t;
Or a case expression:
select (case when sum(minutes) < 24*60 then sum(minutes else 24*60 end)
from t;

Postgresql - Query to multiply time by number

I'm creating a query to multiply a time by a number.
The time is the result of a query of the annual working hours of a person. The purpose is calculate the 25% then I want to multiply the time by 0.25
Example:
Time1: 100:00:00
[format H:M:s]
For this I've tried this query:
SELECT '100:00:00'::time * 0.25
PostgreSQL returns:
ERROR: the time / date value is out of range: «100: 00: 00»
LINE 2: SELECT '100: 00: 00' :: time * 0.25
The error is clear. My conversion of the time is out of range because the time object only is valid to 24 hours. In this case is working fine:
SELECT '24:00:00'::time * 0.25
Result:
"06:00:00"
Someone have any idea?
Time values are limited to 24 hours. You can do the calculation using other units. For instance, the following translates this to seconds:
select 0.25 * ( (string_to_array('100:00:00', ':'))[1]::int*60*60 +
(string_to_array('100:00:00', ':'))[2]::int*60 +
(string_to_array('100:00:00', ':'))[3]::int
) as num_seconds
Note that you also cannot represent the result as a time
You can use an INTERVAL, as in:
select interval '100 hours 0 minutes 0 seconds' * 0.25
or:
select interval '100:00:00' * 0.25
or:
select '100:00:00'::interval * 0.25
The result (for all of them):
?column?
--------
25:00:00

Access query to partition data and sum each partition?

I have a query with the fields date hour and value.
It looks something like this
date hour value
xx/xx/xx 15 100
xx/xx/xx 30 122
xx/xx/xx 45 50
... 100 100
... 115 23
... ... ...
... ... ...
... 2400 400
... 15 23
Basically, date is the date, hour is the hour, and value is the value for that particular 15 minute interval. What I have been trying to figure out is a way to take each hour (so 15, 30, 45, and 100) or (1015, 1030, 1045, 1100) [As you can see hours are military-esque 1:00pm is 1300 and midnight 2400], and sum their values together. So i am looking to return something like this:
xx/xx/xx 100 372
xx/xx/xx 200 23 + (130 data) + (145 data) + (200 data)
And so on...
The table has on average around 100 days and they all start from 15 to 2400 incrementing by 15 with varying numbers for the value column.
I have thought about using a partition, group by, etc. with no real ideas how to tackle it. Essentially I have to take 4 rows (an hour), sum their values, spit out the date, hour, and summed value then repeat for every day. I am not asking for code, just some help with what i should be using since this seems like a simple problem minus the key to solving it.
Any help is greatly appreciated, Thank you!
Grouping by Hour/100 will almost get you there - subtract 1 from the hour will make 1 AM fall to 99, and get included in the grouping. This will give a query that looks like this:
SELECT Table1.Dte, Int(([tme]-1)/100) AS Hr, Sum(Table1.Val) AS TotVal
FROM Table1
GROUP BY Table1.Dte, Int(([tme]-1)/100);
I may have misremembered how you cast to int in Access, but this might work:
Select
[date],
100 * (1 + Cint(([Hour] - 1) / 100)),
Sum(Value)
From
Query
Group By
[date],
100 * (1 + Cint(([Hour] - 1) / 100))
Order By
1, 2
SELECT
DateCol,
Int(HourCol \ -100) * -100 AS Hr,
Sum(Value) AS Value
FROM
YourTable
GROUP BY
DateCol,
Int(HourCol \ -100) * -100
Or you can use ((Hr + 99) \ 100) * 100.