Blog coding and discussion of coding about JavaScript, PHP, CGI, general web building etc.

Friday, February 5, 2016

SQL Aggregates OVER and PARTITION

SQL Aggregates OVER and PARTITION


All,

This is my first post on Stackoverflow, so go easy...

I am using SQL Server 2008.

I am fairly new to writing SQL queries, and I have a problem that I thought was pretty simple, but I've been fighting for 2 days. I have a set of data that looks like this:

UserId          Duration(Seconds)        Month  1               45                       January  1               90                       January  1               50                       February  1               42                       February  2               80                       January  2               110                      February  3               45                       January  3               62                       January  3               56                       January  3               60                       February  

Now, what I want is to write a single query that gives me the average for a particular user and compares it against all user's average for that month. So the resulting dataset after a query for user #1 would look like this:

UserId         Duration(seconds)        OrganizationDuration(Seconds)        Month  1              67.5                     63                                   January  1              46                       65.5                                 February  

I've been batting around different subqueries and group by scenarios and nothing ever seems to work. Lately, I've been trying OVER and PARTITION BY, but with no success there either. My latest query looks like this:

select Userid,          AVG(duration) OVER () as OrgAverage,         AVG(duration) as UserAverage,         DATENAME(mm,MONTH(StartDate)) as Month              from table.name               where YEAR(StartDate)=2014              AND userid=119                     GROUP BY MONTH(StartDate), UserId       

This query bombs out with a "Duration' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause" error.

Please keep in mind I'm dealing with a very large amount of data. I think I can make it work with CASE statements, but I'm looking for a cleaner, more efficient way to write the query if possible.

Thank you!

Answer by Adi T for SQL Aggregates OVER and PARTITION


missing partition clause in Average function

OVER ( Partition by MONTH(StartDate))   

Answer by Ruskin for SQL Aggregates OVER and PARTITION


You are joining two queries together here:

  • Per-User average per month
  • All Organisation average per month

If you are only going to return data for one user at a time then an inline select may give you joy:

SELECT AVG(a.duration) AS UserAvergage,     (SELECT AVG(b.Duration) FROM tbl b WHERE MONTH(b.StartDate) = MONTH(a.StartDate)) AS OrgAverage       ...      FROM tbl a      WHERE userid = 119       GROUP BY MONTH(StartDate), UserId  

Note - using comparison on MONTH may be slow - you may be better off having a CTE (Common Table Expression)

Answer by C Sharper for SQL Aggregates OVER and PARTITION


I was able to get it done using a self join, There's probably a better way.

Select UserId, AVG(t1.Duration) as Duration, t2.duration as OrgDur, t1.Month   from #temp t1  inner join (Select Distinct MONTH, AVG(Duration) over (partition by Month) as duration  from #temp) t2 on t2.Month = t1.Month  group by t1.Month, t1.UserId, t2.Duration   order by t1.UserId, Month desc  

Here's using a CTE which is probably a better solution and definitely easier to read

With MonthlyAverage  as   (  Select MONTH, AVG(Duration) as OrgDur   from #temp  group by Month  )    Select UserId, AVG(t1.Duration) as Duration, m.duration as OrgDur , t1.Month   from #temp t1  inner join MonthlyAverage m on m.Month = t1.Month  group by UserId, t1.Month, m.duration  

Answer by Karthik Kola for SQL Aggregates OVER and PARTITION


Please try this. It works fine to me.    WITH C1  AS  (  SELECT   AVG(Duration) AS TotalAvg,   [Month]  FROM [dbo].[Test]  GROUP BY [Month]  ),  C2  AS  (  SELECT Distinct UserID,  AVG(Duration) OVER(PARTITION BY UserID, [Month] ORDER BY UserID) AS DetailedAvg,   [Month]  FROM [dbo].[Test]  )  SELECT C2.*, C1.TotalAvg  FROM C2 c2   INNER JOIN C1 c1 ON c1.[Month] = c2.[Month]  ORDER BY c2.UserID, c2.[Month] desc;  

Answer by Karthik Kola for SQL Aggregates OVER and PARTITION


You can try below with less code.

SELECT Distinct UserID,  AVG(Duration)  OVER(PARTITION BY [Month]) AS TotalAvg,  AVG(Duration) OVER(PARTITION BY UserID, [Month] ORDER BY UserID) AS DetailedAvg,   [Month]  FROM [dbo].[Test]  


Fatal error: Call to a member function getElementsByTagName() on a non-object in D:\XAMPP INSTALLASTION\xampp\htdocs\endunpratama9i\www-stackoverflow-info-proses.php on line 72

0 comments:

Post a Comment

Popular Posts

Powered by Blogger.