I have this functional t-sql query that counts the entries in a group by clause, and at the same time produces a percentage of the count compared to the entire set.
It is blazing fast (~90 ms) in Azure. I'd like to implement in a similar manner with LINQ to SQL, but I can't figure it out...
select f.worktype, f.counted, (100.0 * f.counted)/ (sum(f.counted) over ()) as percentage
from
(SELECT
wa.skillEN AS workType,
count(wa.skillEN) counted
FROM [dbo].WorkAssignments as WA
join [dbo].WorkOrders as WO ON (WO.ID = WA.workorderID)
WHERE wo.dateTimeOfWork < ('1/1/2014')
and wo.dateTimeOfWork > ('1/1/2013')
and wo.statusEN = 'Completed'
group by wa.skillEN) as f
group by f.worktype, f.counted
The LINQ I've been trying in LINQPad...
WorkAssignments
.Where(wa => wa.WorkOrder.DateTimeofWork > DateTime.Now.AddYears(-2)
&& wa.WorkOrder.DateTimeofWork < DateTime.Now)
.GroupBy(wa => wa.SkillEN)
.Select(g => new
{
label = g.Key,
count = g.Count()
})
.GroupBy(g => new {g.label, g.count})
.Select(gg => new
{
label = gg.Key.label,
count = gg.Key.count,
pct = gg.Sum(a => a.count)
})
(The dates in the where clause are slightly different, but I don't think it's relevant)
So, how would I implement the over () feature in LINQ to SQL?
I have a SQL Query to extract sorted data by sum of a column value. I have a table named CustomerPoint and It has 2 columns named CusTomerID and Point , I want to extract the person with the highest SUM of points. Here is my SQL Query and it runs properly. But I need to execute it in EF6.3 with LambdaExpressions or Linq queries.
SELECT SUM(Point) AS POINTS, CustomerID FROM CustomerPoint AS POINTS GROUP BY CustomerID ORDER BY POINTS
Thank you for your help!
Something like this:
from p in context.Points
group p by p.CustomerID into gr
select new { CustomerID = gr.Key, POINTS = gr.Sum(c=>c.Point) } into re
orderby re.POINTS
Please try this.
from cp in db.CustomerPoints
Group cp by cp.CusTomerID into cpg
select new { CusTomerID = cpg.Key, Points = cpg.Sum(c => c.Point)}
orderby cpg.Points
And lambda form for diversity:
var query = youContext.CustomerPoints
.GroupBy(x => x.CustomerID)
.Select(x => new { Points = x.Sum(y => y.Point),
CustomerID = x.Key })
.OrderBy(x => x.Points);
I'm working with the following dataset:
ID SearchTags
1 Cats,Birds,Dogs,Snakes,Roosters
2 Mice,Chickens,Cats,Lizards
3 Birds,Zebras,Sheep,Horses,Monkeys,Chimps
4 Lions,Tigers,Bears,Chickens
5 Cats,Goats,Pandas
6 Birds,Zebras,Sheep,Horses
7 Rats,Dogs,Hawks,Eagles,Tigers
8 Cats,Tigers,Dogs,Pandas
9 Dogs,Beavers,Sharks,Vultures
10 Cats,Bears,Bats,Leopards,Chickens
I need to query out a list of the most popular SearchTags.
I have a query which will return the most popular SearchTags but it returns the whole list of words. (which I expected). Is it possible to split the SearchTags column on (,) and generate a list of the most popular tags so that I end up with a list/count as follows?:
Cats 5
Dogs 4
Chickens 3
Tigers 3
Bears 2
Sharks 1
etc...
instead of what I get now:
Cats,Birds,Dogs,Snakes,Roosters 1
Dogs,Beavers,Sharks,Vultures 1
Cats,Bears,Bats,Leopards,Chickens 1
etc...
Here's the query that returns the list of words.
SELECT SearchTags, COUNT(*) AS TagCount
FROM Animals
GROUP BY SearchTags
ORDER BY TagCount DESC
I'm using SQL Server. I'd prefer a query but can create a stored procedure if needed.
Thanks for any help you can offer.
You have tagged the question with C# and LINQ, if you have the data in a DataTable then you can do:
DataTable dt = GetDataTableFromDB();
var query = dt.AsEnumerable()
.Select(r => r.Field<string>("SearchTags").Split(','))
.SelectMany(r => r)
.GroupBy(r => r)
.Select(grp => new
{
Key = grp.Key,
Count = grp.Count()
});
If you have LINQ TO SQL set up then you can do:
var query = db.YourTable
.Select(r=> r.SearchTags)
.AsEnumerable()
.Where(r=> !string.IsNullOrWhiteSpace(r))
.Select(r => r.Split(','))
.SelectMany(r => r)
.GroupBy(r => r)
.Select(grp => new
{
Key = grp.Key,
Count = grp.Count()
});
});
This will load all the SearchTags in memory and then you would be able to apply Split.
You can also filter out null or empty string values for SearchTags at your database end like:
var query = db.YourTable
.Where(r=> r.SearchTags != null && r.SearchTags.Trim() != "")
.Select(r=> r.SearchTags)
.AsEnumerable()
.Select(r => r.Split(','))
.SelectMany(r => r)
.GroupBy(r => r)
.Select(grp => new
{
Key = grp.Key,
Count = grp.Count()
});
});
The above will filter out the null or empty strings/only white spaces, from the returned collection at the database end and would work more efficiently.
For filtering out dates do:
DateTime dt = DateTime.Today.AddDays(-14);
var query = db.YourTable
.Where(r=> r.SearchTags != null &&
r.SearchTags.Trim() != "" &&
r.MediaDate >= dt)
.Select(r=> r.SearchTags)
.AsEnumerable()
.Select(r => r.Split(','))
.SelectMany(r => r)
.GroupBy(r => r)
.Select(grp => new
{
Key = grp.Key,
Count = grp.Count()
});
});
Assuming you want TSQL...
There are numerous TSQL functions for splitting strings, but anything using XQuery are by far the fastest versus the plethora of looping functions.
I use something similar to this in a production system on a table with a 10-15K CSV values, and it runs in seconds, versus an old looping function which sometimes took up to a minute.
Anyway, here's a quick demo to get you going.
DECLARE #DATA TABLE (ID INT, SEARCHTAGS VARCHAR(100))
INSERT INTO #DATA
SELECT 1,'Cats,Birds,Dogs,Snakes,Roosters' UNION ALL
SELECT 2,'Mice,Chickens,Cats,Lizards' UNION ALL
SELECT 3,'Birds,Zebras,Sheep,Horses,Monkeys,Chimps' UNION ALL
SELECT 4,'Lions,Tigers,Bears,Chickens' UNION ALL
SELECT 5,'Cats,Goats,Pandas' UNION ALL
SELECT 6,'Birds,Zebras,Sheep,Horses' UNION ALL
SELECT 7,'Rats,Dogs,Hawks,Eagles,Tigers' UNION ALL
SELECT 8,'Cats,Tigers,Dogs,Pandas' UNION ALL
SELECT 9,'Dogs,Beavers,Sharks,Vultures' UNION ALL
SELECT 10,'Cats,Bears,Bats,Leopards,Chickens'
;WITH TagList AS
(
SELECT ID, Split.a.value('.', 'VARCHAR(max)') AS String
FROM (SELECT ID,
CAST ('<M>' + REPLACE(CAST(SEARCHTAGS AS VARCHAR), ',', '</M><M>') + '</M>' AS XML) AS String
FROM #DATA) AS A
CROSS APPLY String.nodes ('/M') AS Split(a)
)
SELECT TOP (10) String, COUNT(*) AS [SearchCount]
FROM TagList
GROUP BY String
ORDER BY [SearchCount] DESC
NB: Anything to do with string manipulation is almost always faster if you can handle it in c#... so the answer by Habib would probably be more efficient than a TSQL solution.
I have the following table (Records):
RecordID int,
Nickname nvarchar(max),
DateAdded datetime
I need group by max count of records for Nickname. I made it:
var users = (from i in db.Records
where i.Form.CompetitionID == cID
group i by i.Nickname into g
orderby g.Count() descending
select new TopUserModel()
{
Nickname = g.Key,
Position = g.Count()
}).Take(100).ToList();
it works
Right now I need to sort it by date too (who first got max records).
I should have a request like it:
select Nickname, Count(*) as Result, MAX(DateAdded) as MDate from Records group by Nickname order by Result Desc, MDate Asc
How to do it by LINQ?
I think this is what you want. I've used extension version of Linq which is probably more easier. The idea is to calculate MaxCount and MaxDate after GroupBy so you can use it in next OrderBy clauses.
db.Records
.Where(i => i.Form.CompetitionID == cID)
.GroupBy(i => i.Nickname)
.Select(g => new { MaxCount = g.Count(), MaxDate = g.Max(i => i.DateAdded), Nickname = g.Key})
.OrderByDescending(gx => gx.MaxCount)
.ThenByDescending(gx => gx.MaxDate)
.Select(gx => new TopUserModel()
{
Nickname = gx.Nickname,
Position = gx.MaxCount
}).Take(100).ToList();
I think what you're asking for is:
...
select new TopUserModel()
{
Nickname = g.Key,
Position = g.Count()
Date = g.Max(r => r.DateAdded)
}).Take(100).OrderByDescending(t => t.Position).ThenBy(t => t.Date).ToList();
When you use group the Key is your grouping but the enumerable is all the records you've grouped so you can still use aggregate functions on them.
If you want to sort by multiple columns, you can put them in order using the chaining above.
var users = (from i in db.Records
where i.Form.CompetitionID == cID
group i by new {i.Nickname} into g
orderby g.Count() descending,
select new TopUserModel()
{
Nickname = g.Key,
Position = g.Count()
Date = g.Max(r => r.DateAdded)
}).Take(100
).OrderBy(c => c.Date).ToList();
Just add max date for nickname to ordering. You also can introduce new range variable for position:
var users = (from r in db.Records
where r.Form.CompetitionID == cID
group r by r.Nickname into g
let position = g.Count()
orderby position descending, g.Max(r => r.DateAdded)
select new TopUserModel {
Nickname = g.Key,
Position = position
}).Take(100).ToList();
Question is already answered here: OrderBy a date field with linq and return ToList()
Add at the end of your users statement
.OrderBy(e => e.Date).ToList();
I want to get a count for each month but count should be only at most one per day even if there are multiple occurences . I have the SQL query which works right but having trouble to convert it into LINQ -
select
count(DISTINCT DAY(date)) as Monthly_Count,
MONTH(date) as Month,
YEAR(date)
from
activity
where
id=#id
group by
YEAR(date),
MONTH(date)
Could anyone help me translating the above query to LINQ. Thanks!
Per LINQ to SQL using GROUP BY and COUNT(DISTINCT) given by #Rick, this should work:
var query = from act in db.Activity
where act.Id == id
group act by new { act.Date.Year, act.Date.Month } into g
select new
{
MonthlyCount = g.Select(act => act.Date.Day).Distinct().Count(),
Month = g.Key.Month,
Year = g.Key.Year
};
I don't know if L2S can convert the inner g.Select(act => act.Date.Day).Distinct.Count() properly.
var results = db.activities.Where(a => a.id == myID)
.GroupBy(a => new
{
Month = a.date.Month,
Year = a.date.Year
})
.Select(g => new
{
Month = g.Key.Month,
Year = g.Key.Year,
Monthly_Count = g.Select(d => d.date.Day)
.Distinct()
.Count()
})