Multiple aggregate functions in the same query using LINQ expression [duplicate] - sql

I want to get count of a set based on different condition:
var invoices = new AccountingEntities().Transactions
var c1 = invoices.Count(i=>i.Type = 0);
var c2 = invoices.Count(i=>i.Type = 1);
var c3 = invoices.Count(i=>i.Type = 2);
How its possible to call all three queries in one DB round trip to increase performance?

Sure, just wrap up your three counts in a POCO or anonymous type:
using (var invoices = new AccountingEntities())
{
var c = (from i in invoices.Transactions
select new
{
c1 = invoices.Count(i=>i.Type = 0),
c2 = invoices.Count(i=>i.Type = 1),
c3 = invoices.Count(i=>i.Type = 2)
}).Single();
}
Also, dispose your context, as I show.

To aggregate arbitrary subqueries, use a dummy single-row result set from which you nest the desired subqueries. Assuming db represents your DbContext, the code to count invoice types will look like this:
var counts = (
from unused in db.Invoices
select new {
Count1 = db.Invoices.Count(i => i.Type == 0),
Count2 = db.Invoices.Count(i => i.Type == 1),
Count3 = db.Invoices.Count(i => i.Type == 2)
}).First();
If the want to generically get a count of all types, use grouping:
var counts =
from i in db.Invoices
group i by i.Type into g
select new { Type = g.Key, Count = g.Count() };

Related

How to speed up querying same table many times with different conditions?

EF6, asp mvc core and SQL Server are used on the background.
I have to do many queries to the same table with different conditions, f.e.
SELECT COUNT(*) FROM Table1 WHERE a = true
SELECT COUNT(*) FROM Table1 WHERE b = true
SELECT COUNT(*) FROM Table1 WHERE a = true || b = true
SELECT a FROM Table1 WHERE b = true
So 4 queries to Table1 with different conditions. I think that as result I have to read the entire Table1 four times. In pseudo code it might be looking like this.
var res1 = new list();
foreach(var rec in Table1)
{
// read Table1 first time
if(rec.a == true)
{
res1.push(rec);
}
}
var res2 = new list();
foreach(var rec in Table1)
{
// read Table1 second time
if(rec.b == true)
{
res2.push(rec);
}
}
var res3 = new list();
foreach(var rec in Table1)
{
// read Table1 third time
if(rec.a == true || rec.b == true)
{
res3.push(rec);
}
}
var res4 = new list();
foreach(var rec in Table1)
{
// read Table1 fourth time
if(rec.b == true)
{
res4.push(rec);
}
}
I want to know how to read the Table1 only one time and get four different results, like this:
var res1 = new List();
var res2 = new List();
var res3 = new List();
var res4 = new list();
foreach(rec in Table1)
{
// read Table1 first time
if(a == true)
{
res1.push(rec);
}
if(b == true)
{
res2.push(rec);
}
if(a == true || b == true)
{
res3.push(rec);
}
if(b == true)
{
res4.push(rec);
}
}
Also the challenge, that those queries are dynamic sql, I mean that a = true, b = true, a = true || b = true are stored in database. And queries are running in this way:
string query = "SELECT Count(*) FROM Table1 WHERE" + condition;
var count = ExecuteSql(query);
The sample above is simplified, but in reality all the query is split and stored in database.
PS. Actually I want to speed up the page, which makes 30-40 requests to the server and each request is the query to the same table. I think if I can replace them with one request instead of 40 requests.
You may use conditional aggregation with just a single query:
SELECT
COUNT(CASE WHEN a = true THEN 1 END) AS cnt_a,
COUNT(CASE WHEN b = true THEN 1 END) AS cnt_b,
COUNT(CASE WHEN a = true OR b = true THEN 1 END) AS cnt_a_b
FROM Table1;
This would reduce the number of full table scans from 3 to just 1. Also, it would also potentially reduce the number of round trips to/from the database from 3 to 1.

Linq Sum based on some columns

I have a query returning some values for a specific CompanyId and a Specific Month/Year.
I want to make a report for the whole year. (So I need to sum up the values from different months, but of the same year and CompanyId)
Here is my query now:
from er in EconomicReports
join com in Companies on er.CompanyId equals com.Id
join cou in Countries on com.CountryId equals cou.Id
where er.Year == 2014
select new
{
Country = cou.Name,
CompanyId = com.Id,
CorporationId = com.CorporationId,
Year = er.Year,
RegisteredCases = er.NewCasesTotalCount,
RegisteredCasesAmount = er.NewCasesTotalAmount,
ResolvedCases = er.ClosedCasesTotalCount,
ResolvedCasesAmount = er.ClosedCasesCapitalAmount + er.ClosedCasesInterestAmount,
ActiveCases = (er.NewCasesTotalCount ?? 0) - (er.ClosedCasesTotalCount ?? 0),
ActiveCasesAmount = (er.NewCasesTotalAmount ?? 0) - (er.ClosedCasesCapitalAmount ?? 0) - (er.ClosedCasesInterestAmount ?? 0)
}
Basically, rows 1 and 5 need to be one row, because they are from the same year, same company Id (I have put also Month in the results for you to see that is a different month, but same year)
you need a GroupBy:
var query = (from er in EconomicReports
join com in Companies on er.CompanyId equals com.Id
join cou in Countries on com.CountryId equals cou.Id
where er.Year == 2014
select new
{
Country = cou.Name,
CompanyId = com.Id,
CorporationId = com.CorporationId,
Year = er.Year,
RegisteredCases = er.NewCasesTotalCount,
RegisteredCasesAmount = er.NewCasesTotalAmount,
ResolvedCases = er.ClosedCasesTotalCount,
ResolvedCasesAmount = er.ClosedCasesCapitalAmount + er.ClosedCasesInterestAmount,
ActiveCases = (er.NewCasesTotalCount ?? 0) - (er.ClosedCasesTotalCount ?? 0),
ActiveCasesAmount = (er.NewCasesTotalAmount ?? 0) - (er.ClosedCasesCapitalAmount ?? 0) - (er.ClosedCasesInterestAmount ?? 0)
});
var result = query
.GroupBy(u => new {u.Country, u.CompanyId, u.CorporationId, u.Year})
.Select(u => new
{
Country = u.Key.Country,
CompanyId = u.Key.CompanyId,
CorporationId = u.Key.CorporationId,
Year = u.Key.Year,
RegisteredCases = u.Select(t => t.RegisteredCases).DefaultIfEmpty().Sum(),
RegisteredCasesAmount = u.Select(t => t.RegisteredCasesAmount).DefaultIfEmpty().Sum(),
ResolvedCases = u.Select(t => t.ResolvedCases).DefaultIfEmpty().Sum(),
ResolvedCasesAmount = u.Select(t => t.ResolvedCasesAmount).DefaultIfEmpty().Sum(),
ActiveCases = u.Select(t => t.ActiveCases).DefaultIfEmpty().Sum(),
ActiveCasesAmount = u.Select(t => t.ActiveCasesAmount).DefaultIfEmpty().Sum()
})
.ToList();
i think Country and CorporationId must be same for grouped records based on CompanyId and Year, so i was included them in group by, to use in Select

What is the equivalent DISTINCT(sql server) in the Linq

I have a Table(Send) with columns(Id, UserId,SendDate) and another table(Receive) with columns(Id,SendId,UserName).
I want show all records in SendTable with all RecieveUserName.
for example.
(Send)
1 1 2013
2 2 2013
(Recieve)
1 1 Jack
2 1 Ema
3 2 Alex
4 2 Sara
Result
1 1 2013 Jack, Ema
2 2 2013 Alex, Sara
I use this query in SqlServer (The DISTINCT keyword eliminates duplicate rows from the results of a SELECT statement)
SELECT DISTINCT c2.Id,
(SELECT STR( UserName )+ ','
FROM dbo.Reciver c1
WHERE c1.SendId = c2.id FOR XML PATH('')) Concatenated, c2.SendDate, c2.UserId
FROM dbo.Send AS c2 INNER JOIN
dbo.Reciver ON c2.Id = dbo.Reciver.SendId
How do this query in Linq?
Distinct is also available in LINQ.
For example
public class Product
{
public string Name { get; set; }
public int Code { get; set; }
}
Product[] products = { new Product { Name = "apple", Code = 9 },
new Product { Name = "orange", Code = 4 },
new Product { Name = "apple", Code = 10 },
new Product { Name = "lemon", Code = 9 } };
var lstDistProduct = products.Distinct();
foreach (Product p in list1)
{
Console.WriteLine(p.Code + " : " + p.Name);
}
Will return all rows.
var list1 = products.DistinctBy(x=> x.Code);
foreach (Product p in list1)
{
Console.WriteLine(p.Code + " : " + p.Name);
}
will return 9 and 4
It doesn't seem to me that you need to use Distinct in this Linq query. Assuming you have the relationships between tables set up on your linq datacontext, you can do something like this:
var result = from s in context.Send
select new {
id = s.Id,
userId = s.UserId,
date = s.SendDate,
users = s.Receive.Select(u => u.UserName)
}
Note: users will an IEnumerable<String> - you can use string.Join() on the client to join the names into a string.
Update
To return users as a string to first need to 'switch' to Linq To Objects by calling AsEnumerable() or ToList() and the Linq to Sql query.
var output = from s in result.AsEnumerable()
select new {
id = s.id,
userId = s.userId,
date = s.date,
users = string.Join(", ", s.users)
}
Also see Gert Arnolds answer for a good explanation.
What you want can only be done in two steps. Not because of the DISTINCT, but because of the FOR XML. The C# equivalent of the latter is String.Join(), but you can't use that in a linq to entities statement directly. So you must collect the required data first, then switch to linq to objects (by applying AsEnumerable) and then do the concatenation and distinct:
db.Sends
.Where(s => s.Receivers.Any())
.Select(s => new {
s.Id,
Concatenated = s.Receivers.Select(r => r.UserName)
s.SendDate,
s.UserId
})
.AsEnumerable()
.Select(x => new {
s.Id,
Concatenated = String.Join(", ", x.Concatenated)
s.SendDate,
s.UserId
})
.Distinct()

How to populate a Chart ( Using System.Web.Helpers.Chart ) in LinqPad?

I have the code below, run it in LinqPad 4.40.03 + Sql Server 2008 R2 + NorthWind.
LinqPad return exception: "ArgumentNullException: Value cannot be null. Parameter name: httpContext".
Please give me the Final Fixed code, I want it populate a chart ( Using System.Web.Helpers.Chart ) in linqpad output screen.
void Main()
{
var q = from p in Products
let s = p.OrderDetails.Sum(o => o.Quantity) * p.UnitPrice
orderby s
select new { ProductName = p.ProductName, Sales = s};
var basicChart = new System.Web.Helpers.Chart(width: 600, height: 400)
.AddTitle("Product Sales")
.DataBindTable(dataSource: q, xField: "ProductName")
.Write();
basicChart.Dump();
}
The ASP.NET charting control relies on HttpContext.Current which is present only when running an ASP.NET application.
You could try mocking an HttpContext, or else use the charting control in System.Windows.Forms.DataVisualization.Charting instead:
var q = from p in Products
let s = p.OrderDetails.Sum(o => o.Quantity) * p.UnitPrice
orderby s
select new { ProductName = p.ProductName, Sales = s};
var chart = new Chart();
var ca = new ChartArea();
ca.AxisX.LabelStyle.Interval = 1;
chart.ChartAreas.Add (ca);
var series = new Series { ChartType = SeriesChartType.Bar};
series.Points.DataBind (q.Take(20), "ProductName", "Sales", null);
chart.Series.Add (series);
chart.Dump ("Chart");

dynamically change LINQ to Entity query

int year = 2009; // get summ of TONS2009 column
var query = from ODInfo in DataContext.CIMS_TRUCKS
where pLocationIDs.Contains(ODInfo.OID)
group ODInfo by ODInfo.OID into g
select new
{
OID = g.Key,
TotalTons = g.Sum( ODInfo => ODInfo.TONS2009)
};
IN the expression 'ODInfo => ODInfo.TONS2009', how do I change TONS2009 to TONS2010 or TONS2011 based on the method parameter 'int year' ?
K06a's answer is close but won't work server-side. Try this:
IEnumerable<OutputType> myQuery(IEnumerable<InputType> data, Expression<Func<InputType,decimal>> expr)
{
return from ODInfo in DataContext.CIMS_TRUCKS
where pLocationIDs.Contains(ODInfo.OID)
group ODInfo by ODInfo.OID into g
select new OutputType
{
OID = g.Key,
TotalTons = g.AsQueryable().Sum(expr)
};
}
var query = myQuery(DataContext.CIMS_TRUCKS, ODInfo => ODInfo.TONS2009);
I haven't tried this, but did something similar here.
UPDATE
If you really need to translate input strings (like "2009") to expressions, it's still possible:
string year = "2009";
Type ODInfoType = typeof(ODINFOTYPE); // substitute with the type of ODInfo
ParameterExpression pe = ParameterExpression.Parameter(ODInfoType, "ODInfo");
MemberInfo mi = ODInfoType.GetProperty("TONS" + year);
MemberExpression me = Expression.MakeMemberAccess(pe, mi);
var expr = Expression.Lambda<Func<ODINFOTYPE, decimal>>(me, pe);
Be aware that this is a patch to the extremly evil structure of your database.
You can try something like that:
TotalTons = g.Sum( ODInfo => (year == 2009) ? ODInfo.TONS2009 : ((year == 2010)
? ODInfo.TONS2010 : ODInfo.TONS2011))
Or make it more readable and use { } to split that lambda expression into more then one line and use eg. switch statement.
The best solution is to break this up into multiple querys that you can compose to a final query:
int year = 2009; // get summ of TONS2009 column
var odInfos =
year == 2009 ? DataContext.CIMS_TRUCKS.Select(x => new { x.OID, TONS = x.TONS2009 })
year == 2010 ? DataContext.CIMS_TRUCKS.Select(x => new { x.OID, TONS = x.TONS2010 })
year == 2011 ? DataContext.CIMS_TRUCKS.Select(x => new { x.OID, TONS = x.TONS2011 })
: null;
var query = from ODInfo in odInfos
where pLocationIDs.Contains(ODInfo.OID)
group ODInfo by ODInfo.OID into g
select new
{
OID = g.Key,
TotalTons = g.Sum(ODInfo => ODInfo.TONS)
};
This will specialize to three possible queries at runtime, thereby giving the best possible performance. It is better than a case-switch.
Try this way:
IEnumerable<OutputType> myQuery(IEnumerable<InputType> data, Func<InputType,decimal> func)
{
return from ODInfo in data
where pLocationIDs.Contains(ODInfo.OID)
group ODInfo by ODInfo.OID into g
select new OutputType
{
OID = g.Key,
TotalTons = g.Sum(func)
};
}
var query = myQuery(DataContext.CIMS_TRUCKS, ODInfo => ODInfo.TONS2009);
Using DynamicLinq which works with EF also:
int year = 2009; // get summ of TONS2009 column
var query = from ODInfo in DataContext.CIMS_TRUCKS
where pLocationIDs.Contains(ODInfo.OID)
group ODInfo by ODInfo.OID into g
select g;
var projectedGroups = query.Select("new (Key as OID, Sum(TONS" + year + ") as TotalTons)");