Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Multiple SQL aggregate functions in a single Linq-to-Entities query

Tags:

In SQL you can express multiple aggregates in a single database query like this:

SELECT MIN(p.x), MAX(p.x), MIN(p.y), MAX(p.y) FROM   Places p JOIN Logs l on p.Id = l.PlaceId WHERE  l.OwnerId = @OwnerId 

Is it possible to do an equivalent thing using Linq-to-Entities? I found a similar question that suggests it's not possible for Linq-to-SQL but I would like to think I won't have to do the above using four round trips to the DB.

like image 797
Drew Noakes Avatar asked Dec 13 '10 17:12

Drew Noakes


People also ask

Can we use multiple aggregate functions in a single query?

For aggregation purposes, there are the SQL aggregate functions. And for multi-level aggregation, you'd use (at least) two aggregate functions at the same time. If you're interested in quality reporting, you'll need much more than SQL's aggregate functions. However, they're certainly the basis of good reporting.

Can we use multiple aggregate function in GROUP BY clause?

This is because GROUP BY will only return unique results per group and the SELECT list can only consist aggregate functions or columns that are part of the GROUP BY clause. So depending on what you want to get, you can use different functions to get the optimal output.

Can we use aggregate function within aggregate function?

Returns an aggregate in a list or database. The AGGREGATE function can apply different aggregate functions to a list or database with the option to ignore hidden rows and error values.

What are aggregate functions in LINQ?

In LINQ, aggregation functions are those functions which are used to calculate a single value from the collection of the values. Real life example of aggregation function is calculating the annual rainfall occurred in 2018 according to readings collected whole year.


2 Answers

Suppose you have the following SQL statement:

  select sum(A), max(B), avg(C) from TBL group by D 

Try this in C#:

  from t in table   group t by D   into g   select new {      s = g.Sum(x => x.A),      m = g.Max(x => x.B),      a = g.Average(x => x.C)   } 

-- or in VB: --

  from t in TBL   group t by key = D   into g = group   select s = g.Sum(function(x) x.A),        m = g.Max(function(x) x.B),        a = g.Average(function(x) x.C) 

The obvious, which in VB would be:

  aggregate t in TBL into s = Sum(t.A), m = Max(t.B), a = Average(t.C) 

though it will give the same results, it has a higher cost as it issues multiple SQL select statements, one for each aggregate function, i.e. it will run in multiple passes. The first syntax, gives a single (fairly complex, but efficient) SQL statement which does a single pass against the database.

PS. If you don't have a key by which to group by (i.e. you need a single row as the result, covering the whole data set), use a constant as in:

  from t in TBL   group t by key = 0   into g = group   select s = g.Sum(function(x) x.A),        m = g.Max(function(x) x.B),        a = g.Average(function(x) x.C) 
like image 93
Costas Avatar answered Sep 28 '22 06:09

Costas


I don't have your DB, but this (using a "default" EDMX model of Northwind.mdb -- no changes after running the new model wizard) runs as one query in LINQPad:

var one = from c in Customers            where c.PostalCode == "12209"           select new           {               Id = c.Orders.Max(o => o.OrderID),               Country = c.Orders.Min(o => o.ShipCountry)           };            one.Dump(); 

Updated, per your comment:

var two = from c in Customers        where c.PostalCode == "12209"       from o in c.Orders       group o by o.Customer.PostalCode into g       select new       {           PostalCode = g.Key,           Id = g.Max(o => o.OrderID),           Country = g.Min(o => o.ShipCountry)       };            two.Dump(); 
like image 29
Craig Stuntz Avatar answered Sep 28 '22 06:09

Craig Stuntz