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.
| 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 hoursSo the report header might look like this once done in Crystal or somethingID --- Monday --- Tuesday and so on1 --- A,B,C --- A,B,C2 --- A,B,C --- A,B,C3 --- 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 intDECLARE @inNumReadingsPerDay intDECLARE @inNumItemsBeingMonitored intSET @inNumDaysData = 10SET @inNumReadingsPerDay = 10SET @inNumItemsBeingMonitored = 2DECLARE @inDayCounter intDECLARE @dtStartDate datetimeDECLARE @inMonitoredItemsCounter intDECLARE @inNumMinsBetweenReadings intDECLARE @inReadingsCounter intDECLARE @dtReadingTimestamp datetimeDECLARE @flRandomNumber floatDECLARE @flReading float--calculate the number of minutes between readingsSET @inNumMinsBetweenReadings = 1440 / @inNumReadingsPerDay--loop through the daysSET @inDayCounter = @inNumDaysDataWHILE @inDayCounter > 1BEGIN --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 - 1END--select the average reading per daySELECT inMonitoredItemID, CAST(CAST(YEAR(dtTimestamp) AS varchar) + '-' + CAST(MONTH(dtTimestamp) AS varchar) + '-' + CAST(DAY(dtTimestamp) AS varchar) AS datetime) As dtTimestamp, AVG(flReading) As flReadingFROM @tblReadingsWHERE 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 advanceKeith |
|
|
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 averageselect inMonitoredItemID ,dateadd(day, datediff(day,0,dtTimestamp),0) ,AVG(flReading) As flReadingfrom @tblReadingswhere datediff(day,dtTimestamp,getdate()) <= 7group by inMonitoredItemID ,dateadd(day, datediff(day,0,dtTimestamp),0)order by 1,2 desc--daily (4 hour segments) averageselect inMonitoredItemID ,dateadd(hour, 4*(datediff(hour,0,dtTimestamp)%4),dateadd(day, datediff(day,0,dtTimestamp),0)) ,AVG(flReading) As flReadingfrom @tblReadingswhere datediff(day,dtTimestamp,getdate()) <= 7group 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 OptimizerTG |
 |
|
|
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 timeKeith |
 |
|
|
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 OptimizerTG |
 |
|
|
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.CheersKeith |
 |
|
|
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=65358here 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 endgocreate function dbo.f_date_from_datetime (@day datetime)returns datetimeasbegin return dateadd(day, datediff(day,0,@day), 0)endgoGive thise functions: try thisdeclare @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 datetimeselect @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 flreadingfrom ( 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 ) awhere dtTimestamp is not nullgroup 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: @time1durrepresent 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/1900the 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 resultsBe One with the OptimizerTG |
 |
|
|
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 againKeith |
 |
|
|
|
|
|
|
|