Average Time over a period of time in a SQL Query - sql

Hello how would i be able to get the AVG of the Minimum time across records?
Meaning, I have these records:
Id Date Time Amount
1 7/1/14 9:00am 5.00
2 7/1/14 8:45am 6.00
3 7/1/14 9:30am 7.00
4 7/2/14 8:30am 4.50
5 7/2/14 9:15am 5.50
6 7/2/14 7:45am 4.75
now what i need is to get MIN of each day... so in this case it would be record 2, and 6.
but i want to some how average those times... meaning 8:45am and 7:45am ...
I want to know what is the average time first sale occurs over a time period. Not sure how to average out the time. So to say, over a given period, generally first sale occurs around say 8:20 am (estimating)
tx very much for any assitance

You can try somthing like this:-
SELECT Id, Date, AVG(Time)
FROM (SELECT Id, Date, MIN(Time) AS Time
FROM YOUR_TABLE
GROUP BY Id, Date) AS TABLE1
GROUP BY Id, Date;
This might be helpful to you.

To get the average, convert the time to seconds, compute the average and convert it back to a time.
SELECT Id, Date, SEC_TO_TIME(AVG(sec))
FROM (SELECT Id, Date, MIN(TIME_TO_SEC(Time)) AS sec
FROM YOUR_TABLE
GROUP BY Id, Date) AS TABLE1
GROUP BY Id, Date;

SELECT Format(( AVG(Table1.daTime) /86400),"hh:nn:ss") AS AvgTimeOfFirstTransaction
FROM (SELECT date, (MIN(Time) *86400) AS daTime
FROM transactions
WHERE ( (transactions.date)>=#7/1/2014# And (transactions.date)<#7/3/2014#)
GROUP BY transactions.date
) AS TABLE1

Related

Extract previous row calculated value for use in current row calculations - Postgres

Have a requirement where I would need to rope the calculated value of the previous row for calculation in the current row.
The following is a sample of how the data currently looks :-
ID
Date
Days
1
2022-01-15
30
2
2022-02-18
30
3
2022-03-15
90
4
2022-05-15
30
The following is the output What I am expecting :-
ID
Date
Days
CalVal
1
2022-01-15
30
2022-02-14
2
2022-02-18
30
2022-03-16
3
2022-03-15
90
2022-06-14
4
2022-05-15
30
2022-07-14
The value of CalVal for the first row is Date + Days
From the second row onwards it should take the CalVal value of the previous row and add it with the current row Days
Essentially, what I am looking for is means to access the previous rows calculated value for use in the current row.
Is there anyway we can achieve the above via Postgres SQL? I have been tinkering with window functions and even recursive CTEs but have had no luck :(
Would appreciate any direction!
Thanks in advance!
select
id,
date,
coalesce(
days - (lag(days, 1) over (order by date, days))
, days) as days,
first_date + cast(days as integer) as newdate
from
(
select
-- get a running sum of days
id,
first_date,
date,
sum(days) over (order by date, days) as days
from
(
select
-- get the first date
id,
(select min(date) from table1) as first_date,
date,
days
from
table1
) A
) B
This query get the exact output you described. I'm not at all ready to say it is the best solution but the strategy employed is to essential create a running total of the "days" ... this means that we can just add this running total to the first date and that will always be the next date in the desired sequence. One finesse: to put the "days" back into the result, we calculated the current running total less the previous running total to arrive at the original amount.
assuming that table name is table1
select
id,
date,
days,
first_value(date) over (order by id) +
(sum(days) over (order by id rows between unbounded preceding and current row))
*interval '1 day' calval
from table1;
We just add cumulative sum of days to first date in table. It's not really what you want to do (we don't need date from previous row, just cumulative days sum)
Solution with recursion
with recursive prev_row as (
select id, date, days, date+ days*interval '1 day' calval
from table1
where id = 1
union all
select t.id, t.date, t.days, p.calval + t.days*interval '1 day' calval
from prev_row p
join table1 t on t.id = p.id+ 1
)
select *
from prev_row

How to spread annual amount and then add by month in SQL

Currently I'm working with a table that looks like this:
Month | Transaction | amount
2021-07-01| Annual Membership Fee| 45
2021-08-01| Annual Membership Fee| 145
2021-09-01| Annual Membership Fee| 2940
2021-10-01| Annual Membership Fee| 1545
the amount on that table is the total monthly amount (ex. I have 100 customers who paid $15 for the annual membership, so my total monthly amount would be $1500).
However what I would like to do (and I have no clue how) is divide the amount by 12 and spread it into the future in order to have a monthly revenue per month. As an example for 2021-09-01 I would get the following:
$2490/12 = $207.5 (dollars per month for the next 12 months)
in 2021-09-01 I would only get $207.5 for that specific month.
On 2021-10-01 I would get $1545/12 = $128.75 plus $207.5 from the previous month (total = $336.25 for 2021-10-01)
And the same operation would repeat onwards. The last period that I would collect my $207.5 from 2021-09-01 would be in 2022-08-01.
I was wondering if someone could give me an idea of how to perform this in a SQL query/CTE?
Assuming all the months you care about exist in your table, I would suggest something like:
SELECT
month,
(SELECT SUM(m2.amount/12) FROM mytable m2 WHERE m2.month BETWEEN ADD_MONTHS(m1.month, -11) AND m1.month) as monthlyamount
FROM mytable m1
GROUP BY month
ORDER BY month
For each month that exists in the table, this sums 1/12th of the current amount plus the previous 11 months (using the add_months function). I think that's what you want.
A few notes/thoughts:
I'm assuming (based on the column name) that all the dates in the month column end on the 1st, so we don't need to worry about matching days or having the group by return multiple rows for the same month.
You might want to round the SUMs I did, since in some cases dividing by 12 might give you more digits after the decimal than you want for money (although, in that case, you might also have to consider remainders).
If you really only have one transaction per month (like in your example), you don't need to do the group by.
If the months you care about don't exist in your table, then this won't work, but you could do the same thing generating a table of months. e.g. If you have an amount on 2020-01-01 but nothing in 2020-02-01, then this won't return a row for 2021-02-01.
CTE = set up dataset
CTE_2 = pro-rate dataset
FINAL SQL = select future_cal_month,sum(pro_rated_amount) from cte_2 group by 1
with cte as (
select '2021-07-01' cal_month,'Annual Membership Fee' transaction ,45 amount
union all select '2021-08-01' cal_month,'Annual Membership Fee' transaction ,145 amount
union all select '2021-09-01' cal_month,'Annual Membership Fee' transaction ,2940 amount
union all select '2021-10-01' cal_month,'Annual Membership Fee' transaction ,1545 amount)
, cte_2 as (
select
dateadd('month', row_number() over (partition by cal_month order by 1), cal_month) future_cal_month
,amount/12 pro_rated_amount
from
cte
,table(generator(rowcount => 12)) v)
select
future_cal_month
, sum(pro_rated_amount)
from
cte_2
group by
future_cal_month

SQL - Counting months passed by a Person Indicator

I'm trying to count the number of months that have passed based on ID, it's possible that for some records the months will not increase by 1 each time (i.e. someone could have a record for 1/1/13 and 3/1/13 but not 2/1/13) however I only want a count of the records in my table. So missing months don't matter.
An example table would be: (notice the missing month and it's irrelevancy).
DATE ID Months Passed
----------- --- --------------
2013-11-01 105 1
2013-12-01 105 2
2014-02-01 105 3
2014-03-01 105 4
Essentially an Excel COUNTIFSin SQL, which I've written:
=COUNTIFS(IDColumn, ID, MonthColumn, "<=" & Month)
Does anyone know of a way to generate the desired column using SQL?
Try ROW_NUMBER(). If you just want the "Months Passed" column to increase by 1 each time, and for each ID, that will do the trick.
SELECT
Date,
Id,
Indicator,
ROW_NUMBER() OVER(PARTITION BY Id ORDER BY Date) AS RowNum
FROM YourTable
WHERE Indicator = 'YES'
UNION
SELECT
Date,
Id,
Indicator,
0 AS RowNum
FROM YourTable
WHERE Indicator = 'NO'
You could more simply count rows grouped by month (more complex if you have count months in different years separately):
SELECT COUNT(derived.monthVal)
FROM (SELECT MONTH(<your date field>) AS monthVal
FROM [your table]
WHERE [Your ID Column] = <the id>
GROUP BY MONTH(<your date field>)) AS derived;

Group by statement to do average of time

My existing database has data coming for an id, value and time. There is one record coming every 3 seconds. I want my select statement to use these data and group them based on id and hrly basis to show the average of the values in that hr. How can I use group by to achieve this ?
This is my sample data:
id value date time
a 5 5/18/2015 10:27:22
a 9 5/18/2015 10:27:25
b 7 5/18/2015 10:27:22
b 8 5/18/2015 10:27:22
I have data coming in every 3 seconds. I want it to be aggregated based on every hr of the day to reflect avg values of that id in that hr.
I want the output to look like
id -a , gives avg of 7 , at 10 on 5/18/2015
This is a relatively simple group by which will have two types of columns generally. Your grouped columns and your aggregates. In this case your grouped columns will have ID,date, and hr(calculated from [time]). You only have one aggregated column in this case: the average of value. Check out my code:
SELECT ID,
[date],
DATEPART(HOUR,[time]) AS hr,
AVG(value) AS avg_val
FROM yourTable
GROUP BY ID,[date],DATEPART(HOUR,[time])
This query will pull each ID along with the average value, grouped by each hour of the day. If you want to run this for more than 1 day, you would have to group by the date + the hour so 6/19/2015 10:00, then 6/19/2015 11:00 and so forth.
SELECT
id,
avg(value) AS avg_val,
datepart(hh, time_interval) AS time_interval
FROM my_table
WHERE time_interval = '6/19/2015'
GROUP BY id, datepart(hh, time_interval)
To include multiple days, you could group change the group by section to be:
GROUP BY id, convert(varchar(10), time_interval, 120), datepart(hh,time_interval)

Can cumulative data for time-bands be calculated using SQL?

I'm wondering if either a running total or total-by-time-block for sales data can be generated using only SQL.
Let's say I have a simple table that records sales and the time they occurred.
ID | Timestamp | Amount
1 | 2014-03-04 09:00:00 | 25.00
2 | 2014-03-04 09:02:25 | 15.00
3 | 2014-03-04 09:13:00 | 5.00
4 | 2014-03-04 09:16:11 | 17.50
5 | 2014-03-04 09:28:18 | 44.50
...
I can easily calculate the total sales for a day with a query like:
SELECT sum(Amount) from Sales
WHERE Timestamp BETWEEN '2014-03-04 00:00:00' AND '2014-03-04 23:59:59'
But I'd like to all calculate the amounts sold during each (say) 15 minute period to get a result like:
08:45 | 0.00
09:00 | 45.00
09:15 | 62:00
...
and a cumulative running total for each (say) 15 minute period to produce a result like:
08:45 | 0:00
09:00 | 40.00
09:15 | 107:00
...
I can write a simple program or use a spreadsheet to achieve these two results given the raw data, but I'm wondering how to do it just using SQL. Is it possible? If so, how?
EDIT: If possible, a DB-agnostic solution would be preferred. I use SQL Server at present.
In SQL Server 2012, you can do this using the cumulative sum window function. You can also get the timeslot in a way that comes close to working in more than one database:
select timeslot,
sum(amount) as amount,
sum(sum(amount)) over (order by timeslot) as cumamount
from (select t.*,
(cast('2014-03-04 00:00:00' as datetime) +
cast( ("timestamp" - cast('2014-03-04 00:00:00' as datetime))*24*4 as int)/(24.0*4)
) as timeslot
from table t
) t
where Timestamp between '2014-03-04 00:00:00' and '2014-03-04 23:59:59'
group by timeslot;
The idea behind the timeslot calculation is to take the difference between timestamp and midnight of some day. This gives the number of days (with fractions) between the two dates. Then multiply this by 24 for hours and 4 for the 15-minute intervals, and it gives the number of 15 minute intervals since midnight on some date. Truncate this value by converting to an integer and add back to the original date. This is all done in a subquery, so the calculation can be repeated.
This approach will work in many databases, though there might be some nuances on the exact expression. The formatting of the datetime would be rather database specific.
The rest is just using the cumulative sum function. If you don't have this, then you can use a correlated subquery instead.
I do not have sol for first request. (total for every "Timeslot" kind of query)
but I do have sol for second request. (cumulative running total for each "Timeslot")
AS Gordon mentioned with SQL server 2012 this is much simpler.
yet as I am providing an old way which can be done on SQL 2005 onward.
Also solution is not 100% database agnostic, but easier to translate from SQL-SERVER to ORACLE or DB2 or anything else.
before going to actual query check out the functions i created to simply give me a TimeSlot values when I give two Date Range. UFN to GET TIMESLOT Values
Note that the function is created at different granularity level by Slot Type. Hour, Minute, Second etc.... you can create new as you like.
In the below sample query I am choosing the timeslot of 11-Seconds.
check the result here. Sample Output
DECLARE #dt TABLE
(
RowID INT IDENTITY NOT NULL
,LastModified DATETIME2(2) NOT NULL
,Amount INT NOT NULL DEFAULT 0
)
INSERT INTO #dt( LastModified, Amount )
SELECT '2014-03-04 00:00:00.00', 10
UNION ALL SELECT '2014-03-04 00:00:05.00', 10
UNION ALL SELECT '2014-03-04 00:00:10.00', 10
UNION ALL SELECT '2014-03-04 00:00:15.00', 10
UNION ALL SELECT '2014-03-04 00:00:20.00', 10
UNION ALL SELECT '2014-03-04 00:00:25.00', 10
UNION ALL SELECT '2014-03-04 00:00:30.00', 10
UNION ALL SELECT '2014-03-04 00:00:35.00', 10
UNION ALL SELECT '2014-03-04 00:00:40.00', 10
UNION ALL SELECT '2014-03-04 00:00:45.00', 10
UNION ALL SELECT '2014-03-04 00:00:50.00', 10
DECLARE #DatePart sysname
,#SlotValue INT
,#MinDt DATETIME2(2)
,#MaxDt DATETIME2(2)
SET #SlotValue = 11
SELECT #MinDt=MIN(LastModified)
,#MaxDt=MAX(LastModified)
FROM #dt
;WITH AllDt(RowID,timeslot,amount)
AS
(
SELECT CAST (ROW_NUMBER() OVER (ORDER BY COALESCE(t1.TimeSlot,t2.LastModified)) AS INT) RowID
,COALESCE(t1.TimeSlot,t2.LastModified)
,ISNULL(t2.Amount,0) AS Amount
FROM dbo.ufn_utl_timeslotBySecond(#SlotValue,#MinDt,#MaxDt) t1
FULL OUTER JOIN #dt t2
ON t1.TimeSlot=t2.LastModified
)
,
RCTE1(RowID,timeslot,amount)
AS
(
SELECT RowID
,timeslot
,Amount
FROM AllDt
WHERE RowID=1
UNION ALL
SELECT dt.RowID,dt.TimeSlot,CAST(dt.Amount+t3.amount AS INT) AS amount
FROM ALLDt dt
JOIN RCTE1 t3
ON dt.RowID=t3.RowID+1
)
SELECT *
FROM RCTE1
ORDER BY TimeSlot