Linq Count and Group by - c#

I have a SQL query that I need to turn into Linq but for the life of me I cannot seem to group the results.
This is the original SQL query:
select UserLevel, count(UserLevel) as count, StudioId
from UserProfile
where CompletedSetup = 1
and userid in (select UserId from webpages_UsersInRoles where RoleId = 4)
group by StudioId, UserLevel
order by StudioId
and what it returns is something like
UserLevel Count StudioId
1 6 1
2 2 1
3 4 1

context.UserProfile
.Where(x=>x.CompletedSetup==1 && context.webpages_UsersInRoles.Any(u=>u.UserId == x.UserId && u.RoleId == 4))
.GroupBy(x=>new {x.UserLevel, x.StudioId)
.Select(x=>new {UserLevel = x.Key.UserLevel, StudioId = x.Key.StudioId, count=x.Count()})

Related

When returning the count from SQL, how to also include the value in grouped item for which the count doesn't exist?

Been working on this SQL dilemma for a while now. The part of the table looks like the following.
It's a many-to-many table relationship where one claim can have many notes. So, one example would be the following:
------------------------------------------
| ClaimID | NoteID | Note |
------------------------------------------
| 2387 | 1 | Test 1 |
| 2387 | 2 | Test 2 |
| 2387 | 3 | Test 3 |
| 2532 | 4 | Something 1 |
| 2539 | 5 | abcd |
| 2539 | 6 | jklm |
------------------------------------------
You get the idea.
So, when I run the query I want the result in such a way that it should show me the number of note counts from 1 to 10. If the count exist, then it should show me the count, otherwise 0. An example of what it would look like in the real-world scenario is the following.
[{
"numOfNotes":1,
"count":5916
},{
"numOfNotes":2,
"count":1846
},{
"numOfNotes":3,
"count":639
},{
"numOfNotes":4,
"count":226
},{
"numOfNotes":5,
"count":94
},{
"numOfNotes":6,
"count":50
},{
"numOfNotes":7,
"count":10
},{
"numOfNotes":8,
"count":2
},{
"numOfNotes":9,
"count":2
},{
"numOfNotes":11,
"count":2
}]
That's the query return from the database that I retrieved using C# and linq. Here's the code for that.
if (type == "e" || type == "p")
{
//sub query to retrieve notes
var subquery = from f in db.DBFileInfo
join c in db.Claims on f.FileID equals c.FileID into cl
from gp1 in cl.DefaultIfEmpty()
join n in db.Notes on gp1.ClaimID equals n.ClaimID into nt
from gp2 in nt.DefaultIfEmpty()
where f.ReportDate.Month == month && f.ReportDate.Year == year
group gp2 by gp2.ClaimID into g
select new
{
Key = g.Key,
Count = g.Count()
};
//query to grop by notes count. Notes count is consider contact per claim
var count = (from c in db.Claims
join s in subquery on c.ClaimID equals s.Key
where c.RecordType == type &&
(c.Username != "RxService")
&& (c.HIC3 != "J3A" && c.HIC3 != "J3C" && c.HIC3 != "H7N")
group s by s.Count into g
orderby g.Key
select new
{
NumOfNotes = g.Key,
count = g.Count()
}).Take(10);
}
If you notice in the result, there are numOfNotes from 1 - 11 but 10 is missing. That's because there aren't any claimID that has 10 notes. So, in this case, I still want SQL to return "numOfNotes": 10, "count": 0. And if you notice, I only asked for 10 results (Take(10)), because there can be more than 10 such notes per claim which we are not interested.
And in some cases, there aren't more than 5 notes per claimID for the given time period. In one instance, the result from SQL only goes up to 6. But I still want the result upto 10 whether it exists or not. Is it possible?
In case if you're interested: Here's my SQL statement
SELECT
count(C.ClaimID) as count, N.NotesPerClaim
FROM
ClaimsTable C
INNER JOIN
(SELECT
claimid, count(note) as NotesPerClaim
FROM
NotesTable
GROUP BY
ClaimID) as N ON N.ClaimID = C.ClaimID
WHERE
RecordType = 'e' AND
(Username <> 'RxService') AND
(HIC3 <> 'J3A' AND HIC3 <> 'J3C' AND HIC3 <> 'H7N')
GROUP BY
N.NotesPerClaim
ORDER BY
N.NotesPerClaim;
You seem to want a numbers table. Here is one method:
WITH nums as (
SELECT 1 as n
UNION ALL
SELECT n + 1
FROM nums
WHERE n < 10
),
t as (
SELECT count(C.ClaimID) as NumClaims, N.NotesPerClaim
FROM ClaimsTable C JOIN
(SELECT claimid, count(note) as NotesPerClaim
FROM NotesTable
GROUP BY ClaimID
) N
ON N.ClaimID = C.ClaimID
WHERE c.RecordType = 'e' AND
c.Username <> 'RxService' AND
c.HIC3 NOT IN ('J3A', 'J3C', 'H7N')
GROUP BY N.NotesPerClaim
)
SELECT nums.n as NotesPerClaim, t.NumClaims
FROM nums LEFT JOIN
t
ON nums.n = t.NotesPerClaim
ORDER BY NotesPerClaim;
Use LEFT JOIN instead of INNER JOIN
Also is better use HIC3 NOT IN ('J3A', 'J3C', 'H7N')
SELECT count(C.ClaimID) AS count
, N.NotesPerClaim
FROM ClaimsTable C
LEFT JOIN (
SELECT claimid
, count(note) AS NotesPerClaim
FROM NotesTable
GROUP BY ClaimID
) AS N ON N.ClaimID = C.ClaimID
WHERE RecordType = 'e'
AND (Username <> 'RxService')
AND HIC3 NOT IN ('J3A', 'J3C', 'H7N')
GROUP BY N.NotesPerClaim
ORDER BY N.NotesPerClaim;
LEFT JOIN on the notes table, and check for ISNULL condition. In the case of NULL from notes, return 0.
Here is a SQL Fiddle demo of the concept.
Your code should be:
`SELECT count(C.ClaimID) as count, ISNULL(N.NotesPerClaim,0) as NotesPerClaim
FROM ClaimsTable C
LEFT OUTER JOIN
(SELECT claimid, count(note) as NotesPerClaim from NotesTable
GROUP BY ClaimID) as N
ON N.ClaimID = C.ClaimID
WHERE RecordType = 'e' AND
(Username <> 'RxService') AND (HIC3 NOT IN ('J3A','J3C','H7N'))
GROUP BY ISNULL(N.NotesPerClaim,0)
ORDER BY N.NotesPerClaim;`

LINQ left join ordered with empty right-items at the end

My understanding of LINQ and Entity Framework is minimal, and I'm learning as I go along...
I am trying to write a query that takes the information from a view called GroupView and does a left-join on a table called GroupSequence... the information is then to be used by an <asp:Repeater>.
The resultant set should have all the items from GroupView with the joined items at the start (in sequence defined by the GroupSequence table) and with non-joined items at the end (in sequence defined by the Id of the GroupView items).
I.e...
[GroupView] | [GroupSequence]
[Id] [Name] [Calc] | [Id] [GroupId] [UserId] [Sequence]
1 Group 1 23 | 1 1 1 3
2 Group 2 34 | 2 2 1 2
3 Group 3 45 | 3 3 1 1
4 Group 4 56
5 Group 5 67
With the expected outcome of...
[Id] [Name] [Calc]
3 Group 3 45
2 Group 2 34
1 Group 1 23
4 Group 4 56
5 Group 5 67
If I do the following, despite using the DefaultIfEmpty, all I get is the 3 groups linked to the sequence. But the page displays, even though it's only 3 rows...
from #group in context.GroupViews
join seq in context.GroupSequences on #group.Id equals seq.GroupId into groupSeq
from item in groupSeq.DefaultIfEmpty()
where item.UserId == 1
orderby item.Sequence
select new { Id = #group.Id, Name = #group.Name, Calc = #group.Calc };
If I do the following, the .DataBind on the repeater complains that...
The entity or complex type 'DevModel.GroupSequence' cannot be constructed in a LINQ to Entities query
from #group in context.GroupViews
join seq in context.GroupSequences on #group.Id equals seq.GroupId into groupSeq
from item in groupSeq.DefaultIfEmpty(new GroupSequence { Id = #group.Id, UserId = 1, Sequence = #group.Id + 1000 })
where item.UserId == 1
orderby item.Sequence
select new { Id = #group.Id, Name = #group.Name, Calc = #group.Calc };
Based on this question and accepted answer I have also tried using a DTO like this...
class GroupViewSequenceDTO
{
public int Id { get; set; }
public string Name { get; set; }
public int? Calc { get; set; }
}
from #group in context.GroupViews
join seq in context.GroupSequences on #group.Id equals seq.GroupId into groupSeq
from item in groupSeq.DefaultIfEmpty(new GroupSequence { Id = #group.Id, UserId = 1, Sequence = #group.Id + 1000 })
where item.UserId == 1
orderby item.Sequence
select new GroupViewSequenceDTO { Id = #group.Id, Name = #group.Name, Calc = #group.Calc };
But I still get the same error as before (cannot be constructed)
The question...
How can I write this query so that the repeater will show all 5 rows, with the first 3 in sequence order, and the last 2 added on? What am I doing wrong?
You need to move the filter into the left join condition since it will be false when item is null.
from #group in context.GroupViews
from seq in context.GroupSequences.Where(x => #group.Id == x.GroupId && x.UserId == 1).DefaultIfEmpty()
orderby seq.Sequence ?? int.MaxValue
select new GroupViewSequenceDTO
{
Id = #group.Id,
Name = #group.Name,
Calc = #group.Calc
};

How to write query for the below scenario in Entity Framework?

Tables:
Users
userid username imageurl
1 venkat http://test.jpg
2 raghu http://test1.jpg
3 ravi http://test2.jpg
Friends
id userid frienduserid status
1 1 2 true
2 2 1 false
LogStatus
id userid sessionid
1 1 7482748
2 1 8274282
3 2 3123123
If I pass userid=1 then need to pull his friends username,imageurl,status and any of his friends having at least one sessionid then give status as "true".
As of I am able to join users, friends table giving his friends username,imageurl and status. But how to check any one of his having at least one sessionid?
My query:
var result = from pa in cxt.Users
join us in cxt.Friends on pa.User_Id equals us.Friend_UserId
where us.User_Id == incID
select new
{
us.frienduserid,
pa.User_Name,
pa.ImageURL,
us.status
};
var result =
from user in cxt.Users
join friend in cxt.Friends on user.UserId equals friend.FriendUserId
where user.UserId == incId
select new
{
FriendUserId = friend.FriendUserId,
UserName = user.UserName,
ImageUrl = user.ImageUrl,
Status = cxt.LogStatus.Any(s=>s.UserId == user.UserId)
};

LINQ to SQL - CASE statement with subquery

I am having trouble trying to represent the below SQL (which returns the results I want) in LINQ:
select
case r.CategoryID
when 2 then
case r.PrimaryRecord
when 1 then r.RecordID
else (select RecordID from Record where RecordGroupID = r.RecordGroupID and PrimaryRecord = 1)
end
else
r.RecordID
end
as RecordID
, r.FooID
from Record r
where
r.FooID = 3
Each row in the Record table has a unique RecordID. Multiple RecordID's could be associated with a RecordGroupID for CategoryID 2, but only one of them will have the PrimaryRecord field value of 1.
Given the below table of data, my desired output is RecordID = 1, FooID = 3, i.e. the RecordID for the given RecordGroupID that is the PrimaryRecord, but the FooID for the given row that matches my Where clause.
RecordID RecordGroupID PrimaryRecord CategoryID FooID
1 1 1 2 1
2 1 0 2 1
3 1 0 2 3
I appreciate the SQL itself probably isn't the most efficient SQL in the world but it was the best I could come up with.
If anyone could help me create the LINQ statement to represent this query that would be great.
I think you don't really need the case in the original query. Try something like:
var matchingRecords = from r in Records
where r.FooId = fooId && r.CategoryId == catId && r.RecordGroupId == groupId
join r2 in Records on r.RecordGroupId == r2.RecordGroupId && r.CategoryId == r2.CategoryId && r2.PrimaryRecord
select r2;
Edit: added CategoryId in join, assuming RecordGroupId is only unique inside a category.

C# Linq finding value

I want to return the depart number that is not found Employee Table by comparing Department table.
Person Table
ID name salary job commision DeptID
--------------------------------------------------------------
P001 Jon 2000 Manager NULL 1
P002 Skeet 1000 Salesman 2000 1
P003 James 2340 Developer NULL 2
P004 greed 4500 Developer NULL 2
P005 Joel 1330 Salesman 1200 1
P006 Deol 5000 Architect NULL 2
Department Table
DeptID DeptName
1 Management
2 Software
3 ERP
SQL
select DeptId from dept
where deptId not in (select deptid from person)
When i try to execute the below code
LINQ
var qry = from n in context.Persons
where n.DeptID !=
(from m in context.Depts select m.DeptId)
select new { DeptID = n.DeptID };
I receive the following error
Operator '!=' cannot be applied to operands of type 'int?' and 'System.Linq.IQueryable'
var qry = from n in context.Persons
where n.DeptID !=
(from m in context.Depts select m.DeptId).FirstOrDefault()
select new { DeptID = n.DeptID };
You are trying to compare DeptID with a collection 1 or more department Ids. Even if there would only logically be one result for a DeptID, syntactically you need to specify that you want the first hit.
Suggested rephrasing:
var q = from m in context.Depts
where
!context.Persons.Select(p => p.DeptID).Contains(m.DeptID)
select new { DeptID = m.DeptID };
It sounds that your DeptID field in SQL is set to allow nulls. In that case you'd probably want something along the lines of this:
var qry = from n in context.Persons
where n.DeptID.Value !=
(from m in context.Depts select m.DeptId)
select new { DeptID = n.DeptID.Value };
I think it should be something like that. I tried to get a list of DeptID's first and then implement a NOT IN with contains :
var deptIDs = context.Persons
.Where( p => !context.Depts
.Select(d => new {DeptID = d.DeptID})
.Contains( p.DeptID )
)
.Select( p => new { DeptID = n.DeptID } );

Categories

Resources