join without forgetting data that wasn't affected by the join - c#

I have three tables I'm interested in, here's the relevant information:
+---------------+ +---------------+ +---------------+ +---------------+
| Class | | Tests | | StudentClasses| | Student |
+---------------+ +---------------+ +---------------+ +---------------+
| ClassID | | ClassID | | ClassID | | StudentID |
| | | WholeYearTest | | StudentID | | StudentYear |
| | | TestYear | | | | |
+---------------+ +---------------+ +---------------+ +---------------+
I have a variable login, which is a Student.
I am running this query in LINQ:
from tests in App.db.Tests
join studentClasses in App.db.StudentClasses on tests.ClassID equals studentClasses.ClassID
where (
(tests.WholeYearTest == true && tests.TestYear == login.StudentYear)
||
studentClasses.StudentID == login.StudentID
)
select tests;
Unfortunately, I'm only getting results which correspond to the condition studentClasses.StudentID == login.StudentID
What I want:
All tests that the student has to sit due to StudentYear being equal to TestYear and WholeYearTest being true
All tests that the student has to sit because one of the possibly multiple StudentClasses associated with them is listed under Tests.ClassID.
I think it's probably due to a blatant misunderstanding of how JOIN works, but I can't think of any other way to implement this. Can anyone put forward an implementation suggestion other than looping all tests? If not, I suppose I'll use a loop, but I'm sure there must be some way of implementing this in LINQ.

I think this will do what you want:
from tests in App.db.Tests
join studentClasses in App.db.StudentClasses
on new { tests.ClassID, login.StudentID }
equals new { studentClasses.ClassID, studentClasses.StudentID }
into gj
from subStudentClass in gj.DefaultIfEmpty()
where (
(tests.WholeYearTest == true && tests.TestYear == login.StudentYear)
||
(subStudentClass != null && subStudentClass.StudentID == login.StudentID)
)
select tests
The biggest difference is that there is now an outer join so that you can still find results where the join fails.
Also, the join is now by both ClassID and StudentID, so that you won't get matches for other students.
With the following test data:
var App = new { db = new {
Tests = new[] {
new Test { ClassID = 1, WholeYearTest = true, TestYear = 1999 },
new Test { ClassID = 2, WholeYearTest = true, TestYear = 1999 },
new Test { ClassID = 3, WholeYearTest = false, TestYear = 1999 },
new Test { ClassID = 4, WholeYearTest = false, TestYear = 1999 },
},
StudentClasses = new[] {
new StudentClass { ClassID = 1, StudentID = 1 },
new StudentClass { ClassID = 1, StudentID = 2 },
new StudentClass { ClassID = 4, StudentID = 1 },
new StudentClass { ClassID = 3, StudentID = 2 },
}
} };
var login = new { StudentID = 1, StudentYear = 1999 };
Console.WriteLine(string.Join(Environment.NewLine, (
//above query
).Select(x => string.Join(",", x.ClassID, x.WholeYearTest, x.TestYear))));
It prints
1,True,1999
2,True,1999
4,False,1999

A single inner join is not sufficient; your query actually needs to be executed with an inner join AND a union.
Give this a try - I didn't test it out because I figure you can do that :)
Note - you may need to insert a Distinct() in there as well.
var q1 = (from tests in App.db.Tests
join studentClasses in App.db.StudentClasses
on tests.ClassID equals studentClasses.ClassID
select tests).Concat(App.db.Tests.Where(t=>t.WholeYearTest && t.TestYear == login.StudentYear));

Related

Query with join, group by and count to linq

im doing this query on sql and it works, but how do i make it to Linq ?
select b.Name,a.idempresa,count(1) as cuenta from Descarga_XML_recepcion_V2 as a
inner join EnterpriseSGE as b on a.idempresa = b.EnterpriseSGEId group by idempresa,b.name
this is what it should bring
name1 | 5041 | 583
name2 | 4031 | 1730
name3 | 5042 | 640
name4 | 4034 | 300
name5 | 6047 | 861
name6 | 5043 | 187
name7 | 4033 | 318
A straight forward translation of the SQL into LINQ yields:
var ans = from a in Descarga_XML_recepcion_V2
join b in EnterpriseSGE on a.idempresa equals b.EnterpriseSGEId
group 1 by new { a.idempresa, b.name } into ingroup
select new {
ingroup.Key.idempresa,
ingroup.Key.name,
cuenta = ingroup.Count()
};
Try :
var results = (from a in Descarga_XML_recepcion_V2
join b in EnterpriseSGE on a.idempresa equal b.EnterpriseSGEId
select new { a = a, b = b})
.GroupBy(x => new { idempresa = x.a.idempresa, name = x.b.name})
.Select(x => new {name = x.Key.name, idempresa = x.Key.idempressa, count = x.Count()})
.ToList();

LINQ select from join table where the same foreign key has records for two different IDs

Ok, so the title is a little bit confusing I guess. Basically I have those 3 tables:
Line
id | Name
---------
1 | "A-B"
2 | "A-D"
Stop
id | Name
---------
1 | A
2 | B
3 | C
4 | D
LineStop
Id | LineId | StopId | Order
----------------------------
1 | 1 | 1 | 0
2 | 1 | 2 | 1
3 | 2 | 1 | 0
4 | 2 | 2 | 1
5 | 2 | 3 | 3
4 | 2 | 4 | 4
So this is some sort of bus ticketing system which I work on of personal improvement.
As an input I get the departure StopId (Stop.Id) and the arrival StopId (Stop.Id). I want to select all lines that has those two stops in their routes (This would mean that in LineSop table for the same LineId I'll have records with both the departuring and arrival stops, ultimately I would also like to consider the Order column which tells in what order the bus is going through those Stops, because even if the line have the two stops I'm interested in, if they are in reversed order I'm still not interested.
I know that is highly desirable to show what I've done so far but I struggle with the where conditions which seems to be the key factor here. For some reason I decided to join Line with LineStop:
var lines = _context.Lines.Join(
_context.LineStop,
line => line.Id,
lineStop => lineStop.LineId,
(line, lineStop) => lineStop)
But then.. I need to check if for the same LineId I have records in LineStop table with the start and end StopId and ultimately when I found such records the the starting StopId Order i less than the end StopId Order.
I hope this can help you out:
First I get the trip from the traveler: "I want go from Stop: 2 to Stop:4".
Once I know the line that has both stops I build the stops and its order.
var lines = new List<Line>()
{
new Line() { Id = 1, Name = "A-B" },
new Line() { Id = 2, Name = "A-D" }
};
var stops = new List<Stop>() {
new Stop() { Id = 1, Name = "A" },
new Stop() { Id = 2, Name = "B" },
new Stop() { Id = 3, Name = "C" },
new Stop() { Id = 4, Name = "D" }
};
var lineStops = new List<LineStop>()
{
new LineStop() { Id = 1, LineId = 1, StopId = 1, Order = 0 },
new LineStop() { Id = 2, LineId = 1, StopId = 2, Order = 1 },
new LineStop() { Id = 3, LineId = 2, StopId = 1, Order = 0 },
new LineStop() { Id = 4, LineId = 2, StopId = 2, Order = 1 },
new LineStop() { Id = 5, LineId = 2, StopId = 3, Order = 3 },
new LineStop() { Id = 4, LineId = 2, StopId = 4, Order = 4 },
};
var result = (from trip in (from l in lines
join d in lineStops on l.Id equals d.LineId
join a in lineStops on l.Id equals a.LineId
where d.StopId == 2 && a.StopId == 4
select new { d.LineId })
join l in lines on trip.LineId equals l.Id
join ls in lineStops on l.Id equals ls.LineId
select new { l.Name, ls.StopId, ls.Order }).OrderBy(x => x.Order);
Expected result
Name StopId Order
A-D 1 0
A-D 2 1
A-D 3 3
A-D 4 4

Unknown column 'Project1.ID' in 'where clause'

I have the following Tables
+--------+ +---------+ +---------+
| Class |1 N| Student |1 N| Test |
+--------+<--->+---------+<--->+---------+
|ClassID | |ClassID | |TestID |
|Desc | |StudentID| |StudentID|
+--------+ |Name | |Name |
+---------+ |Score |
+---------+
and I need to determine the total score of the first student of a class. there can be one, multiple or none existing tests for this student
So the result should look like
ClassDesc | StudentName | ScoreCount
----------+-------------+-----------
C1 | Max | 201
C2 | Tom | null
I have the following code
using (myEntities ctx = new myEntities())
{
var Reslut = ctx.Class
.Select(x => new
{
ClassDesc = x.Desc,
StudentName = x.Student.FirstOrDefault().Name,
ScoreCount = x.Student.FirstOrDefault().Test.Sum(t => t.Score) //Error here
}).ToList();
}
but it gives me the error
Unknown column 'Project1.ClassID' in 'where clause'
Try the following:
var result = (from cls in ctx.Class
join stdt in ctx.Student on cls.ClassID equals stdt.ClassID
select new
{
ClassDesc = cls.Desc,
StudentName = stdt.Name,
ScoreCount = stdt.Test.Sum(t => t.Score)
}).ToList();
Which should generate SQL similar to the following:
SELECT
Class.ClassID,
Class.[Desc],
Student.Name,
(
SELECT
SUM(Test.Score)
FROM dbo.Test
WHERE Student.StudentID = Test.StudentID
) AS ScoreSum
FROM dbo.Class
INNER JOIN dbo.Student ON Class.ClassID = Student.ClassID

Need help writing nested SQL query in C#

I need some help with building an SQL query. Here's the scenario.
In this only table I have, there are over 22 columns. For now I'm only focusing on 3:
---------------------------------------------------------
| id | Status | EditNumber |
---------------------------------------------------------
| A0001 | Approved | AG01 |
| A0002 | Approved | AG02 |
| A0003 | Approved | AG01 |
| A0004 | Approved | AG03 |
| A0005 | Other | AG01 |
| A0006 | Other | AG02 |
| A0007 | Pending | AG01 |
| A0008 | Pending | AG03 |
| A0009 | Denied | AG04 |
| A0010 | Denied | AG03 |
| A0011 | Approved | AG02 |
| A0012 | Approved | AG01 |
| A0013 | Approved | AG03 |
| A0014 | Denied | AG01 |
| A0015 | Pending | AG04 |
| A0016 | Pending | AG01 |
---------------------------------------------------------
This is just an example. Now, what I need is the count of each EditNumber and the count of each status for each EditNumber
This is what I have done.
var subquery = from n in db.Claims
group n by new { n.id, EditNumber = n.EditNumber.Substring(0, 4) } into g
select new
{
Key = g.Key,
Count = g.Count()
};
var count = (from c in db.Claims
join s in subquery on c.ClaimID equals s.Key.ClaimID
group s by s.Key.EditNumber into g
join e in db.EditOverrides on g.Key equals e.EditNumber
orderby g.Count() descending
select new EOSummaryCount
{
EditNumber = g.Key,
Count = g.Count(),
Description = e.Description
}).AsEnumerable();
but this only gives me the count of EditNumber.
and the following:
db.C.Where(w => w.RecordType == type)
.GroupBy(x => x.Status)
.Select(s => new StatusCount { Status = s.Key, Count = s.Count() });
only gives me the status count. But only gives me in format i.e. in JSON I see the result as
{
"key":"PENDING",
"count":93
},{
"key":"CLOSED",
"count":128
},{
"key":"MEDREVIEW",
"count":218
},{
"key":"APPROVED",
"count":3946
},{
"key":"DENIED",
"count":746
}
What I ultimately want in JSON format is something like this:
[{
"editNumber":"AG001",
"count":2195,
"status":[
{
"key":"INPROCESS",
"count":3
},{
"key":"PENDING",
"count":93
},{
"key":"CLOSED",
"count":128
},{
"key":"MEDREVIEW",
"count":218
},{
"key":"APPROVED",
"count":3946
},{
"key":"DENIED",
"count":746
}]
},{
"editNumber":"AG002",
"count":234,
"status":[
{
"key":"INPROCESS",
"count":3
},{
"key":"PENDING",
"count":93
},{
"key":"CLOSED",
"count":0
},{
"key":"MEDREVIEW",
"count":218
},{
"key":"APPROVED",
"count":104
},{
"key":"DENIED",
"count":30
}]
}]
How can I get this result? I couldn't figure out how I could join those two together to get a new result with the above JSON. Btw, it's all in WebApi controller
that returns HttpResponseMessage.
Without seeing your table structure, you could try this:
var result = db.Claims
.GroupBy(c => c.EditNumber)
.Select(g => new {
editNumber = g.FirstOrDefault().EditNumber,
count = g.Count(),
status = g.GroupBy(x => x.Status).Select(sg => new {
key = sg.FirstOrDefault().Status,
count = sg.Count(),
})
}).ToList();
You can do it as below
from p in Samples
group p.Status by p.EditNumber into g
select new { EditNumber = g.Key, EditCount= g.Count(),
StatusList = from x in g.ToList() group x by x into xc select new { Status = xc.Key, StatusCount= xc.Count() } }

Combine tables using row values as column LINQ C# SQL

I have a users table:
Id | Name | Age
--------------------
1 | Steve | 21
2 | Jack | 17
3 | Alice | 25
4 | Harry | 14
I also have a table containing additional user info:
UId | Key | Value
----------------------
1 | Height | 70
2 | Height | 65
2 | Eyes | Blue
4 | Height | 51
3 | Hair | Brown
1 | Eyes | Green
The UId column links to the Id column in the users table. As you can see, not all users have the same additional info present. Alice doesn't have a height value, Jack is the only one with an eye color value etc.
Is there a way to combine this data into one table dynamically using C# and LINQ queries so that the result is something like this:
Id | Name | Age | Height | Eyes | Hair
------------------------------------------
1 | Steve | 21 | 70 | Green |
2 | Jack | 17 | 65 | Blue |
3 | Alice | 25 | | | Brown
4 | Harry | 14 | 51 |
If a user does not have a value for the column, it can remain empty/null. Does this require some sort of data pivoting?
For the case, your user info fields are constant:
var result = users.GroupJoin(details,
user => user.Id,
detail => detail.Id,
(user, detail) => new
{
user.Id,
user.Name,
user.Age,
Height = detail.SingleOrDefault(x => x.Key == "Height").Value,
Eyes = detail.SingleOrDefault(x => x.Key == "Eyes").Value,
Hair = detail.SingleOrDefault(x => x.Key == "Hair").Value,
});
You can do it by utilising GroupJoin, example:
var users = new List<Tuple<int, string, int>> {
Tuple.Create(1, "Steve", 21),
Tuple.Create(2, "Jack", 17),
Tuple.Create(3, "Alice", 25),
Tuple.Create(4, "Harry", 14)
};
var userInfos = new List<Tuple<int, string, string>> {
Tuple.Create(1, "Height", "70"),
Tuple.Create(2, "Height", "65"),
Tuple.Create(2, "Eyes", "Blue"),
Tuple.Create(4, "Height", "51"),
Tuple.Create(3, "Hair", "Brown"),
Tuple.Create(1, "Eyes", "Green"),
};
var query = users.GroupJoin(userInfos,
u => u.Item1,
ui => ui.Item1,
(u, infos) => new { User = u, Infos = infos });
var result = query.Select(qi => new
{
Id = qi.User.Item1,
Name = qi.User.Item2,
Age = qi.User.Item3,
Height = qi.Infos.Where(i => i.Item2 == "Height").Select(i => i.Item3).SingleOrDefault(),
Eyes = qi.Infos.Where(i => i.Item2 == "Eyes").Select(i => i.Item3).SingleOrDefault(),
Hair = qi.Infos.Where(i => i.Item2 == "Hair").Select(i => i.Item3).SingleOrDefault()
});
First of all I have grouped the user details data using Feature (I have renamed the Key property with Feature to avoid confusion) & UId then I have used group join to combine both results using into g. Finally retrieved the result using specified feature.
var result = from user in users
join detail in details.GroupBy(x => new { x.UId, x.Feature })
on user.Id equals detail.Key.UId into g
select new
{
Id = user.Id,
Name = user.Name,
Age = user.Age,
Height = g.FirstOrDefault(z => z.Key.Feature == "Height") != null ?
g.First(z => z.Key.Feature == "Height").First().Value : String.Empty,
Eyes = g.FirstOrDefault(z => z.Key.Feature == "Eyes") != null ?
g.First(z => z.Key.Feature == "Eyes").First().Value : String.Empty,
Hair = g.FirstOrDefault(z => z.Key.Feature == "Hair") != null ?
g.First(z => z.Key.Feature == "Hair").First().Value : String.Empty,
};
I am getting following output:-
Here is the complete Working Fiddle.
Try this
var list = (from u in context.users
join ud in context.UserDetails on u.Id equals ud.UId
select new
{
u.Id,
u.Name,
u.Age,
ud.Key,
ud.Value
});
var finallist = list.GroupBy(x => new { x.Id, x.Name,x.Age}).Select(x => new
{
x.Key.Id,
x.Key.Name,
x.Key.Age,
Height = x.Where(y => y.Key == "Height").Select(y => y.Value).FirstOrDefault(),
Eyes = x.Where(y => y.Key == "Eyes").Select(y => y.Value).FirstOrDefault(),
Hair = x.Where(y => y.Key == "Hair").Select(y => y.Value).FirstOrDefault()
}).ToList();
try this query
var objlist=( form a in contex.user
join b in contex.UserDetails on a.id equals a.Uid into gj
from subpet in gj.DefaultIfEmpty()
select new { Id=a.id, Name=a.name, Age=a.age, Height =subpet.Height,Eyes=subpet.Eyes, Hair=subpet.Hair}).ToList();

Categories

Resources