Please start any new threads on our new site at https://forums.sqlteam.com. We've got lots of great SQL Server experts to answer whatever question you can come up with.

 All Forums
 SQL Server 2000 Forums
 Transact-SQL (2000)
 Avg per day - easy, Avg for parts of day is not

Author  Topic 

k420
Starting Member

32 Posts

Posted - 2006-05-18 : 05:58:49
Hi All,

I'm having one of those moments where my "get me by" SQL knowledge is not getting me by

To set the scene - I have a system which collects readings for items and the readings table simply contains the item id, timestamp and reading. I have created some SQL which returns the average reading per item per day over the last 7 days without any problems.

I now need to create a report which will show the average reading per item, 3 times a day over the last 7 days. E.g. Each day needs to have an average for

- 07:30 for the last 4 hours
- 15:00 for the last 4 hours
- 20:30 for the last 4 hours

So the report header might look like this once done in Crystal or something

ID --- Monday --- Tuesday and so on
1 --- A,B,C --- A,B,C
2 --- A,B,C --- A,B,C
3 --- A,B,C --- A,B,C

I'm totally lost with how to do this so I'm hoping one of you guys can help out.

Here is some SQL to create a temp table, populate it with random readings and then get the daily average.

DECLARE @tblReadings TABLE
(
inReadingID int IDENTITY,
inMonitoredItemID int,
dtTimestamp datetime,
flReading float
PRIMARY KEY (inReadingID)
)

DECLARE @inNumDaysData int
DECLARE @inNumReadingsPerDay int
DECLARE @inNumItemsBeingMonitored int

SET @inNumDaysData = 10
SET @inNumReadingsPerDay = 10
SET @inNumItemsBeingMonitored = 2

DECLARE @inDayCounter int
DECLARE @dtStartDate datetime
DECLARE @inMonitoredItemsCounter int
DECLARE @inNumMinsBetweenReadings int
DECLARE @inReadingsCounter int
DECLARE @dtReadingTimestamp datetime
DECLARE @flRandomNumber float
DECLARE @flReading float

--calculate the number of minutes between readings
SET @inNumMinsBetweenReadings = 1440 / @inNumReadingsPerDay

--loop through the days
SET @inDayCounter = @inNumDaysData
WHILE @inDayCounter > 1
BEGIN
--calculate the date
SET @dtStartDate = DATEADD(dd, -@inDayCounter, CAST(CAST(YEAR(GETDATE()) AS varchar) + '-' + CAST(MONTH(GETDATE()) AS varchar) + '-' + CAST(DAY(GETDATE()) AS varchar) AS datetime))

--loop through the number of items being monitored
SET @inMonitoredItemsCounter = 1
WHILE @inMonitoredItemsCounter <= @inNumItemsBeingMonitored
BEGIN
--loop through the minutes
SET @inReadingsCounter = 1
WHILE @inReadingsCounter <= @inNumReadingsPerDay
BEGIN
--generate the reading timestamp
SET @dtReadingTimestamp = DATEADD(mi, (@inNumMinsBetweenReadings * @inReadingsCounter), @dtStartDate)

--generate a random reading
SET @flRandomNumber = RAND()
SELECT @flReading = ((30 + 1) - 5) * @flRandomNumber + 5

--insert the data into the readings table
INSERT INTO @tblReadings (inMonitoredItemID, dtTimestamp, flReading)
VALUES (@inMonitoredItemsCounter, @dtReadingTimestamp, @flReading)

--increment the readings counter
SET @inReadingsCounter = @inReadingsCounter + 1
END

--increment the monitored items counter
SET @inMonitoredItemsCounter = @inMonitoredItemsCounter + 1
END

--decrement the day counter to move closer to today
SET @inDayCounter = @inDayCounter - 1
END

--select the average reading per day
SELECT inMonitoredItemID, CAST(CAST(YEAR(dtTimestamp) AS varchar) + '-' + CAST(MONTH(dtTimestamp) AS varchar) + '-' + CAST(DAY(dtTimestamp) AS varchar) AS datetime) As dtTimestamp, AVG(flReading) As flReading
FROM @tblReadings
WHERE dtTimestamp > DATEADD(dd, -7, GETDATE())
GROUP BY inMonitoredItemID, CAST(CAST(YEAR(dtTimestamp) AS varchar) + '-' + CAST(MONTH(dtTimestamp) AS varchar) + '-' + CAST(DAY(dtTimestamp) AS varchar) AS datetime)
ORDER BY inMonitoredItemID, CAST(CAST(YEAR(dtTimestamp) AS varchar) + '-' + CAST(MONTH(dtTimestamp) AS varchar) + '-' + CAST(DAY(dtTimestamp) AS varchar) AS datetime) DESC


Thanks for your time in advance

Keith

TG
Master Smack Fu Yak Hacker

6065 Posts

Posted - 2006-05-18 : 07:37:35
quote:
- 07:30 for the last 4 hours
- 15:00 for the last 4 hours
- 20:30 for the last 4 hours

I'm confused about what you want here because those times are not in 4 hour increments?

Here is some code for your daily average (should be a little more efficient than what you have) As well as some code to break that down further to 4 hour segments. It may give you some ideas for how to accomplish what you need.

--daily average
select inMonitoredItemID
,dateadd(day, datediff(day,0,dtTimestamp),0)
,AVG(flReading) As flReading
from @tblReadings
where datediff(day,dtTimestamp,getdate()) <= 7
group by inMonitoredItemID
,dateadd(day, datediff(day,0,dtTimestamp),0)
order by 1,2 desc

--daily (4 hour segments) average
select inMonitoredItemID
,dateadd(hour, 4*(datediff(hour,0,dtTimestamp)%4),dateadd(day, datediff(day,0,dtTimestamp),0))
,AVG(flReading) As flReading
from @tblReadings
where datediff(day,dtTimestamp,getdate()) <= 7
group by inMonitoredItemID
,dateadd(hour, 4*(datediff(hour,0,dtTimestamp)%4),dateadd(day, datediff(day,0,dtTimestamp),0))
order by 1,2 desc


Be One with the Optimizer
TG
Go to Top of Page

k420
Starting Member

32 Posts

Posted - 2006-05-18 : 08:18:32
Thanks for the response TG.

The reason the times are not in 4 hours increments is because the customer who wants the reports ideally wants to be able to specify any 3 times of the day and any average period. I just randomly picked some times and said average over 4 hours in my example but it could be the same times of day with averages for the previous 2 hours from each time instead or totally different times altogether. I hope that makes more sense now and hopefully you can tell me whether it is possible or not cos I'm still not sure.

Now to the code. I want to understand the responses that I get on the forums rather than just use the code without worring about it and the following line has me confused:
dateadd(day, datediff(day,0,dtTimestamp),0)

I understand that this line is creating the per day grouping but I don't really follow how it works. Please can you explain the theory behind how diffing with 0 and then adding 0 gets you the day at midnight.

Seeing as I'm not sure about the line above you can probably guess that I'm a bit confused about this one as well:
dateadd(hour, 4*(datediff(hour,0,dtTimestamp)%4),dateadd(day, datediff(day,0,dtTimestamp),0))


Again, thanks for your time

Keith
Go to Top of Page

TG
Master Smack Fu Yak Hacker

6065 Posts

Posted - 2006-05-18 : 09:30:54
I'm glad you want to blindly use downloaded code
See if breaking out the segments helps you understand. It is basically adding the the number of days from "0" (1/1/1900) to "0" (1/1/1900). So the result is today without the time component. I'll have to give you ulimate question a little more thought...

select getdate() [getdate()]
,convert(datetime, 0) [convert(datetime, 0)]
,datediff(day,0,getdate()) [datediff(day,0,getdate())]
,dateadd(day, datediff(day,0,getdate()),0) [dateadd(day, datediff(day,0,getdate()),0)]


getdate() convert(datetime, 0) datediff(day,0,getdate()) dateadd(day, datediff(day,0,getdate()),0)
-------------------------------------------------------------------------------- -----------------------------------------
2006-05-18 09:27:49.020 1900-01-01 00:00:00.000 38853 2006-05-18 00:00:00.000




Be One with the Optimizer
TG
Go to Top of Page

k420
Starting Member

32 Posts

Posted - 2006-05-18 : 11:05:28
Ahh, I understand now. Very sneaky!

I look forward to seeing if the averaging at seemingly random intervals is possible.

Cheers

Keith
Go to Top of Page

TG
Master Smack Fu Yak Hacker

6065 Posts

Posted - 2006-05-18 : 12:17:58
this solution requires the following function:
http://www.sqlteam.com/forums/topic.asp?TOPIC_ID=65358

here are some other great datetime related functions (thanks MVJ!)
http://www.sqlteam.com/forums/topic.asp?TOPIC_ID=64762


--and here is another one this solution requires:
if objectproperty(object_id('dbo.f_date_from_datetime'),'IsScalarFunction') = 1
begin drop function dbo.f_date_from_datetime end
go
create function dbo.f_date_from_datetime (@day datetime)
returns datetime
as
begin
return dateadd(day, datediff(day,0,@day), 0)
end
go


Give thise functions: try this


declare @time1dur int --minutes from 12:00 am
,@time1end datetime
,@time2dur int --minutes from 12:00 am
,@time2end datetime
,@time3dur int --minutes from 12:00 am
,@time3end datetime
select @time1dur = 240
,@time1end = '1900-01-01 07:30:0.000'
,@time2dur = 240
,@time2end = '1900-01-01 15:00:0.000'
,@time3dur = 240
,@time3end = '1900-01-01 20:30:0.000'

select inMonitoredItemID
,dtTimestamp
,AVG(flReading) As flreading
from (
select inMonitoredItemID
,case
when dbo.F_time_from_datetime(dtTimestamp) between dateadd(minute,-1*@time1dur, @time1end) and @time1end then dbo.f_date_from_datetime(dtTimestamp) + @time1end
when dbo.F_time_from_datetime(dtTimestamp) between dateadd(minute,-1*@time2dur, @time2end) and @time2end then dbo.f_date_from_datetime(dtTimestamp) + @time2end
when dbo.F_time_from_datetime(dtTimestamp) between dateadd(minute,-1*@time3dur, @time3end) and @time3end then dbo.f_date_from_datetime(dtTimestamp) + @time3end
end as dtTimestamp
,flReading
from @tblReadings
where datediff(day,dtTimestamp,getdate()) <= 7
) a
where dtTimestamp is not null
group by inMonitoredItemID
,dtTimestamp


EDIT:
sorry for the lack of explanation, we were having power issues and I wanted to post this before my UPS died.

the parameters like: @time1dur
represent the number of previous minutes to include in each of the 3 time "buckets". So 240 is 4 hours.

the parameters like: ,@time1end
represent the 3 time offsets from 1/1/1900

the functions are just helpers to extract the day component from a datetime and the time component from a datetime.

Your report writer should be able to "pivot" the results to your desired layout.

I basically just used a derived table to translate each dtTimestamp into one of your 3 time buckets. Then aggreated those results

Be One with the Optimizer
TG
Go to Top of Page

k420
Starting Member

32 Posts

Posted - 2006-05-19 : 03:24:01
Thank you for solving that one for me. I was expecting something huge and horrible but its actually quite clear and straight forward.

Slowly but surely, with the help of the people on this forum, I feel like I'm getting there with my SQL skills. However, I also feel like I'm only just scratching the surface

Thanks again

Keith

Go to Top of Page
   

- Advertisement -