Good morning all,
I have been stuck on this all morning and feel like I've hit a wall. I'd love any advice that can be given at this point.
My table is basically as follows:
PatientName|LivingSpace
-----------|-----------
Patient 1 | Unit 1
Patient 2 | Unit 1
Patient 3 | Unit 2
Patient 4 | Unit 2
Patient 5 | Unit 3
Patient 6 | Unit 3
Patient 7 | Unit 3
Patient 8 | Unit 3
I need a LINQ to SQL query to illustrate this:
Unit|Count
----|-----
Unit 1 | 2
Unit 2 | 2
Unit 3 | 4
TOTAL | 8
My SQL query works fine, I'm just having issues with converting it to LINQ:
SELECT LivingSpace, COUNT(LivingSpace) AS LivingSpace
FROM PatientTable
WHERE Status = 'Active'
GROUP BY LivingSpace
UNION ALL
SELECT 'SUM' LivingSpace, COUNT(LivingSpace)
FROM PatientTable
var counts = from x in ctx.PatientTable
group x by x.LivingSpace into y
select new { Key = y.Key Count = y.Count() };
var total = new { Key = "Total" , Count = ctx.PatientTable.Count() };
var full = counts.ToList();
full.Add(total);
If you want to do it all in one query the following should work (adjusting for the actual names of your properties of course).
context.PatientTable.GroupBy(a => a.LivingSpace.Name, a => 1)
.Select(a => new
{
a.Key,
Total = a.Sum(q => q)
})
.Union(PatientTable.Select(a => new
{
Key = "Total",
Total = PatientTable.Count()
}))
var report = patients
.GroupBy(p => p.LivingSpace)
.Select(g => new
{
Unit = g.Key,
Count = g.Count()
})
.Union(patients
.Select(p => new
{
Unit = "Total",
Count = patients.Count
}));
Something like this should work and just run one query.
var results = db.PatientTable
.GroupBy(p => p.LivingSpace)
.Select(grp => new
{
Unit = grp.Key,
Count = grp.Count()
})
.Union(db.PatientTable
.GroupBy(p => 1)
.Select(grp => new
{
Unit = "Total",
Count = grp.Count()
}));
I see you got the answer, but for learning purposes, here is side by side conversion.
Your SQL (with some aliases added for better comparison)
SELECT P.LivingSpace, COUNT(P.*) AS Count
FROM PatientTable AS P
WHERE P.Status = 'Active'
GROUP BY P.LivingSpace
UNION ALL
SELECT 'SUM' AS LivingSpace, COUNT(P.*) AS Count
FROM PatientTable AS P
The same single query in LINQ
var query =
(
from p in db.PatientTable
where p.Status = "Active"
group p by p.LivingSpace into g
select new { LivingSpace = g.Key, Count = g.Count() }
)
.Concat
(
from p in db.PatientTable
group p by "SUM" into g
select new { LivingSpace = g.Key, Count = g.Count() }
);
Related
Lets say I have the following EF6 Linq statement which counts number of items for 2 tables (Table1 has 10 items and Table2 has no items) :
var q = db.Table1.GroupBy(g => "Table1").Select(g => new { Name = g.Key, EntryCount = g.Count() })
.Union(db.Table2.GroupBy(g => "Table2").Select(g => new { Name = g.Key, EntryCount = g.Count() }));
var r = q.ToList();
The expected result should be something like
Name | EntryCount
---------------------
Table1 | 10
Table2 | 0
However because Table2 doesn't have any items it doesn't appear in the final result and I get the following:
Name | EntryCount
---------------------
Table1 | 10
How can I make sure Table 2 always appear in the final list even if its empty and doesn't have any records?
To give you a background on why I am doing this Linq statement , I am converting the following TSQL statement into a linq query:
CREATE FUNCTION [dbo].[ufnGetLookups] ()
RETURNS
#lookupsWithItemCounts TABLE
(
[Name] VARCHAR(100),
[EntryCount] INT
)
AS
BEGIN
INSERT INTO #lookupsWithItemCounts([Name],[EntryCount])
VALUES
('Table1', (SELECT COUNT(*) FROM Table1)),
('Table2', (SELECT COUNT(*) FROM Table2)),
('Table3', (SELECT COUNT(*) FROM Table3))
RETURN;
END
Also its very important to for this linq statement to run in one database trip, not multiple.
If you have a list of tables then table list can be used to union a row with EntryCount = 0 and then on final result set a GroupBy on Name and Sum of EntryCount will provide desired result.
//List of tables
var tableList = new string[] { "Table1", "Table2" };
var res = db.Table1
.GroupBy(t1 => "Table1")
.Select(gt1 => new { Name = gt1.Key, EntryCount = gt1.Count()})
.Union(db.Table2
.GroupBy(t2 => "Table2")
.Select(gt2 => new { Name = gt2.Key, EntryCount = gt2.Count()})
)
.Union(tableList
.GroupBy(s => s)
.Select(gs => new { Name = gs.Key, EntryCount = 0 })
)
.GroupBy(gg => gg.Name)
.Select(fg => new {Name = fg.Key, EntryCount=fg.Select(ee => ee.EntryCount).Sum()})
.ToList();
The result will have EntryCount for all tables in list.
You can do this by creating a default table with a single row in it.
var ans = Table1.GroupBy(u => "Table1")
.Select(ug => new { Name = ug.Key, EntryCount = ug.Count() })
.Union(Table2.GroupBy(l => "Table2")
.Select(lg => new { Name = lg.Key, EntryCount = lg.Count() })
.Union(OneRowTable.GroupBy(u2 => 1)
.Select(u2g => new { Name = "Table2", EntryCount = u2g.Count()-1 }) )
.OrderByDescending(cg => cg.EntryCount)
.Take(1)
);
This is evaluated in a single round trip to the database by LINQ to SQL. I can't easily test with LINQ to EF.
Note that in EF Core 3.0, the original query is translated in such a way as to return a 0 row for any empty tables.
Hi I have some two tables,
Product:
ProductID | IssueDate | Amount
1 2017-06-01 1000
2 2017-06-01 1000
3 2017-06-02 500
and Credit:
ProductID | Amount
1 500
1 500
2 1000
3 500
if I use query like this from SQL Server:
SELECT p.IssueDate, SUM(p.Amount), SUM(p.Total)
FROM (SELECT p.IssueDate, SUM(p.Amount) AS Amount,
(SELECT TOP 1 SUM(c.Amount) FROM Credit c WHERE p.Id = c.ProductId) AS Total from Product p
GROUP BY p.IssueDate, p.Id) p
GROUP BY p.IssueDate
I get this result:
IssueDate | Amount | Total
2017-06-01 2000 2000
2017-06-02 500 500
In C# Linq I can get this data by two queries like this:
var data = from p in Products.Collection
select new
{
Product = p,
Credits = Credit.Collection.Where(c => c.ProductID == p.ID).Sum(c => c.Amount)
};
var result = from d in data
group d by new
{
IssueDate = d.Product.IssueDate
} into gr
select new
{
IssueDate = gr.Key.IssueDate,
Credits = gr.Sum(s => s.Credits),
Total = gr.Sum(s => s.Product.Amount)
};
var test = result.ToList();
Does anyone know a better (simpler) solution to get this result? Maybe in one query?
How about:
Products.Collection.GroupBy(x => x.IssueDate)
.Select(x => new
{
IssueDate = x.Key,
Amount = x.Sum(p => p.Amount),
Total = Credit.Collection.Where(c => x.Any(p => p.ProductID == c.ProductID)).Sum(c => c.Amount)
}).ToList();
I've got the following data
title | useful
ttitle1 | Yes
ttitle1 | Yes
ttitle1 | No
ttitle2 | Yes
I would like to group the above data and flatten it so I get the following result:
Title | Useful Count | Not Useful Count
tttitle1 | 2 | 1
tttitle2 | 1 | 0
Tried this, but it does not produce the correct result:
var query = (from r in ratings
group r by new { r.ArticleTitle, r.Useful } into results
group results by new { results.Key.ArticleTitle } into results2
from result in results2
select new
{
Title = result.Key.ArticleTitle,
Yes = result.Select(i => i.Useful).Count(),
No = result.Select(i => i.Useful == false).Count()
});
Any help?
It seems to me that the only problem is that you're grouping twice. I'd expect this to work:
var query = from rating in ratings
group rating by rating.ArticleTitle into g
select new
{
Title = g.Key,
Yes = g.Count(r => r.Useful),
No = g.Count(r => !r.Useful)
};
Or not in query expression form:
var query = ratings.GroupBy(r => r.ArticleTitle,
(key, rs) => new
{
Title = key,
Yes = rs.Count(r => r.Useful),
No = rs.Count(r => !r.Useful)
});
You don't need to group twice to get the desired result. One Grouping would be fine:
var query = (from r in ratings
group r by new { r.ArticleTitle } into g
from result in groups
select new
{
Title = result.Key,
Yes = result.Select(i => i.Useful).Count(),
No = result.Select(i => !i.Useful).Count()
});
I have table 4 columns.
JobId StateId Salary Expense
1 1 35,000 31,000
1 1 33,000 25,000
1 2 28,000 26,000
2 2 7,000 16,000
2 2 6,000 20,000
2 1 9,000 22,000
2 1 15,000 29,000
By using LINQ in C#, i want to group by JobId and StateId combination.For each combination i want an array of Salary and array of Expense.
I can get one column as a array by for each combination, by using this
(from r in myTable.AsEnumerable()
group r by new {
jobId = r.Field<int>("JobId"),
stateId = r.Field<int>("StateId")
}).ToDictionary(
l => Tuple.Create(l.Key.jobId, l.Key.stateId),
l=> (from i in l select i.Field<double>("Salary")).AsEnumerable()
);
How can i have Salary and Expense in two array for each group??
My goal is to find average Salary and average Expense for each combination and do some other operation. Or at least tell me how can select multiple columns as separate array.
Note: I don't want collection of anonymous objects for each combination.
To select two different columns as collections in your query you can do this:
var result =
(from r in myTable.AsEnumerable()
group r by new
{
jobId = r.Field<int>("JobId"),
stateId = r.Field<int>("StateId")
} into g
select new
{
g.Key,
Salaries = g.Select(x => x.Field<double>("Salary")),
Expenses = g.Select(x => x.Field<double>("Expense"))
})
.ToDictionary(
l => Tuple.Create(l.Key.jobId, l.Key.stateId),
l => new { l.Salaries, l.Expenses }
);
Then you can compute the averages fairly easily:
var averageSalary = result[...].Salaries.Average();
var averageExpense = result[...].Expenses.Average();
But if all you really need is the averages, this will work:
var result =
(from r in myTable.AsEnumerable()
group r by new
{
jobId = r.Field<int>("JobId"),
stateId = r.Field<int>("StateId")
} into g
select new
{
g.Key,
AverageSalary = g.Average(x => x.Field<double>("Salary")),
AverageExpense = g.Average(x => x.Field<double>("Expense"))
})
.ToDictionary(
l => Tuple.Create(l.Key.jobId, l.Key.stateId),
l => new { l.AverageSalary, l.AverageExpense }
);
Do not use LINQ for this kind of statement. If you need to count the groupings and the compute some sort of average salary/expense you could try a list:
List<myType> myList = new List<myType>();
//add stuff to myList
List<myType> JobID1 = new List<myType();
List<myType> JobID2 = new List<myType();
foreach(var item in myList)
{
if(item.JobID == 1)
JobID1.add(item);
if(item.JobID == 2)
JobID2.add(item);
}
int avgSalOne;
foreach(var item in JobID1)
{
avgSalOne += item.Salary;
}
avgSalOne = avgSaleOne / JobID2.Count;
//Note that you get Job Id 2 average salary the same way, and also the Expense by changing item. Salary to item.Expense
I want to use LINQ to group data from a DataTable (columns: userid, chargetag, charge).
The content could look like this:
userid chargetag charge
-----------------------------
user1 tag3 100
user2 tag3 100
user3 tag5 250
I need something like this as a result:
chargetag count sum
-------------------------
tag3 2 200
tag5 1 250
This is what I have so far:
var groupedData = from b in dataTable.AsEnumerable()
group b by b.Field<string>("chargetag") into g
let count = g.Count()
select new
{
ChargeTag = g.Key,
Count = count,
};
I can extract the name of the chargetag and the number of it.
How would I have to change the LINQ query to access the sum of charges as well?
Thanks in advance :-)
Regards,
Kevin
That's pretty easy - just use the Sum extension method on the group.
var groupedData = from b in dataTable.AsEnumerable()
group b by b.Field<string>("chargetag") into g
select new
{
ChargeTag = g.Key,
Count = g.Count(),
ChargeSum = g.Sum(x => x.Field<int>("charge"))
};
(I've removed the let clause here as it wasn't really buying you anything.)
Now that may be inefficient; it may end up grouping twice in order to perform two aggregation operations. You could fix that like with a query continuation like this, if you really wanted:
var groupedData = from b in dataTable.AsEnumerable()
group b by b.Field<string>("chargetag") into g
select new
{
ChargeTag = g.Key,
List = g.ToList(),
} into g
select new
{
g.ChargeTag,
Count = g.List.Count,
ChargeSum = g.List.Sum(x => x.Field<int>("charge"))
};
Or with a let clause instead:
var groupedData = from b in dataTable.AsEnumerable()
group b by b.Field<string>("chargetag") into g
let list = g.ToList()
select new
{
ChargeTag = g.Key,
Count = list.Count,
ChargeSum = list.Sum(x => x.Field<int>("charge"))
};