Trying to do a single query which combined data from multiple joins into a single property
Rig
----
RigId
Component1Id
Component2Id
Component3Id
Work Item
---------
Id
ComponentID
Description
I'm trying to do a query that returns a list of rigs with a single property called history that consists of all the workItems associated with the components in a Rig.
I cant seem to do multiple conditions in a join or do separate joins and concatenate the items into a single property.
So the result is something like
RigId, History (which consists of a list of all the workitems for the rig)
Here is the answer in query syntax:
var ans = from r in Rigs
join w in WorkItems on r.Component1ID equals w.ComponentID into wg1
join w in WorkItems on r.Component2ID equals w.ComponentID into wg2
join w in WorkItems on r.Component3ID equals w.ComponentID into wg3
select new { r.RigID, History = wg1.Concat(wg2).Concat(wg3).ToList() };
and if you prefer, lambda syntax (this was a bit harder...)
var ans2 = Rigs.GroupJoin(WorkItems, r => r.Component1ID, w => w.ComponentID, (r, w1g) => new { r, h1 = w1g.ToList() })
.GroupJoin(WorkItems, rh1 => rh1.r.Component2ID, w => w.ComponentID, (rh1, w2g) => new { rh1.r, h2 = rh1.h1.Concat(w2g.ToList()) })
.GroupJoin(WorkItems, rh2 => rh2.r.Component3ID, w => w.ComponentID, (rh2, w3g) => new { rh2.r.RigID, History = rh2.h2.Concat(w3g.ToList()) });
I don't think using columns for the components if a very good idea - what happens when a Rig has more or fewer than 3 components? You should really have a separate RigComponent table.
Related
I want to change code below to be sql translateable because now i get exception.
Basicallly i want list of customers from certain localisation and there could be more than one customer with the same CustomerNumber so i want to take the one that was most recently added.
In other words - distinct list of customers from localisation where "distinct algorithm" works by taking the most recently added customer if there is conflict.
The code below works only if it is client side. I could move Group By and Select after ToListAsync but i want to avoid taking unnecessary data from database (there is include which includes list that is pretty big for every customer).
var someData = await DbContext.Set<Customer>()
.Where(o => o.Metadata.Localisation == localisation)
.Include(nameof(Customer.SomeLongList))
.GroupBy(x => x.CustomerNumber)
.Select(gr => gr.OrderByDescending(x => x.Metadata.DateAdded).FirstOrDefault())
.ToListAsync();
Short answer:
No way. GroupBy has limitation: after grouping only Key and Aggregation result can be selected. And you are trying to select SomeLongList and full entity Customer.
Best answer:
It can be done by the SQL and ROW_NUMBER Window function but without SomeLongList
Workaround:
It is because it is not effective
var groupingQuery =
from c in DbContext.Set<Customer>()
group c by new { c.CustomerNumber } into g
select new
{
g.Key.CustomerNumber,
DateAdded = g.Max(x => x.DateAdded)
};
var query =
from c in DbContext.Set<Customer>().Include(x => x.SomeLongList)
join g in groupingQuery on new { c.CustomerNumber, c.DateAdded } equals
new { g.CustomerNumber, g.DateAdded }
select c;
var result = await query.ToListAsync();
I am trying to query a table with nested linq query. My query working but is too slow. I have almost 400k row. And this query work 10 seconds for 1000 rows. For 400k I think its about to 2 hours.
I have rows like this
StudentNumber - DepartmentID
n100 - 1
n100 - 1
n105 - 1
n105 - 2
n107 - 1
I want the students which have different department ID. My results looks like this.
StudentID - List
n105 - 1 2
And my query provides it. But slowly.
var sorgu = (from yok in YOKAktarim
group yok by yok.StudentID into g
select new {
g.Key,
liste=(from birim in YOKAktarim where birim.StudentID == g.Key select new { birim.DepartmentID }).ToList().GroupBy (x => x.DepartmentID).Count()>1 ? (from birim in YOKAktarim where birim.StudentID == g.Key select new { birim.DepartmentID }).GroupBy(x => x.DepartmentID).Select(x => x.Key).ToList() : null,
}).Take(1000).ToList();
Console.WriteLine(sorgu.Where (s => s.liste != null).OrderBy (s => s.Key));
I wrote this query with linqpad C# statement.
For 400K records you should be able to return the student ids and department ids into an in-memory list.
var list1 = (from r in YOKAktarim
group r by new { r.StudentID, r.DepartmentID} into g
select g.Key
).ToList();
Once you have this list, you should be able to group by StudentID and select those students who have more than one record.
var list2 = (from r in list1 group r by r.StudentID into g
where g.Count() > 1
select new
{
StudentID = g.Key,
Departments = g.Select(a => a.DepartmentID).ToList()
}
).ToList();
This should be faster as it only hits the sql database once, rather than hundreds of thousands of times.
You're iterating your source collection (YOKAktarim) three times, which makes your query *O(n^3)` query. It's going to be slow.
Instead of going back to source collection to get content of the group you can simply iterate over g.
var sorgu = (from yok in YOKAktarim
group yok by yok.StudentID into g
select new {
g.Key,
liste = from birim in g select new { birim.DepartmentID }).ToList().GroupBy (x => x.DepartmentID).Count()>1 ? (from birim in g select new { birim.DepartmentID }).GroupBy(x => x.DepartmentID).Select(x => x.Key).ToList() : null,
}).Take(1000).ToList();
However, that's still not optimal, because you're doing a lot of redundant subgrouping. Your query is pretty much equivalent to:
from yok in YOKAktarim
group yok by yok.StudentID into g
let departments = g.Select(g => g.DepartmentID).Distinct().ToList()
where departments.Count() > 1
select new {
g.Key,
liste = departments
}).Take(1000).ToList();
I can't speak for the correctness of that monster, but simply removing all ToList() calls except the outermost one will fix your issue.
The following join projection is throwing the error, "The 'GroupJoin' operation must be followed by a 'SelectMany' operation where the collection selector is invoking the 'DefaultIfEmpty' method." I've run over a few permutations of changes, but haven't been able to figure it out. Thoughts?
EDIT: It's looking more and more like this may be a Dynamics CRM issue. The Xrm models I'm accessing for data have been generated by the CRM SDK (CRM 2011).
var q =
left
.GroupJoin(right,
c => c.Id,
cl => cl.c.Id,
(c, cs) => new { c, cs })
.Where(x=>x.c.Name.Contains("some text"))
.SelectMany(x => x.cs.DefaultIfEmpty(), (x, csubl) =>
new
{
CompanyName = x.c.Name
});
i think in query syntax it will be more beautiful
var q = from l in left
join r in right on l.Id equals r.c.Id into groupped
from g in groupped.DefaultIfEmpty()
where l.Name.Contains("some text")
select new {
CompanyName = l.Name;
}
UPDATE
in samples from msdn have sample with left join, so you can try my code above, or move Where before GroupJoin or after SelectMany
I am trying to get from 3 related tables by using LINQ. But when I use 2 joins, the result takes only elements getting from 2nd join. Here is my code:
var myAssList = mldb.Assigns
.Join(mldb.Lists,
a => a.list_id,
l => l.id,
(a, l) => new {
Assign = a,
List = l
})
.Where(a => a.Assign.assigned_to == "myname")
.Join(mldb.Elements,
li => li.List.id,
e => e.parent_server_id,
(li, e) => new {
Element = e
});
var jsonSerialiser = new JavaScriptSerializer();
var listListJson = jsonSerialiser.Serialize(myAssList);
this Json return only attributes from Element(e) and List(li). But I want to get also the attributes from Assign(a).
The SQL query I am trying to realize in LINQ is that:
select * from Assigns
inner join Lists
on Assigns.server_list_id=Lists.id
inner join Elements
on Lists.id=Elements.parent_id
where Assigns.assigned_to='myname'
So, how can I get the attributes from the first join also (from "a", "l" and "e")?
You can access Assign entity from outer sequence li variable:
.Join(mldb.Elements,
li => li.List.id,
e => e.parent_server_id,
(li, e) => new {
Element = e,
Assign = li.Assign // here
});
from a in mldb.Assigns
join l in mldb.Lists on a.list_id equals l.id
join e in mldb.Elements on l.id equals e.parent_server_id
where a => a.Assign.assigned_to == "myname"
select new { Assign = a, Element = e }
This is so called "query syntax". It makes LINQ expressions looks like SQL queries.
In the end they are translated to IEnumerable extension methods. If you want
to join multiple tables then query syntax is more readable. Another useful feature
of query syntax is let clause. With the aid of it, you can declare additional variables
inside your queries.
I have a LINQ statement I am trying to get right, so maybe going about this all wrong. My objective is to query a table and join in another table to get counts.
Places
ID, Display
ProfilePlaces
ID, PlaceID, Talk, Hear
Basically Places have ProfilePlaces in a one to many relationship. I want to get the number SUM of ProfilePlaces that have Talkand Hear. Talkand Hear are bit fields.
The following gives me a unique list of Places, so I need to add in the Talkand Hear counts.
var counts = from p in db.Places
join pp in db.ProfilePlaces on p.ID equals pp.PlaceID
group new { Place = p } by p.Display;
I thought something like this, but not having any luck
var counts = from p in db.Places
join pp in db.ProfilePlaces on p.ID equals pp.PlaceID
group new { Place = p,
Talk = pp.Count(t => t.Talk == true),
Hear = pp.Count(t => t.Hear == true)
} by p.Display;
Thanks for any help.
You want to do a GROUP JOIN to get the counts for each Place.
var counts2 = from p in places
join pp in profilePlaces on p.ID equals pp.PlaceID into g
select new
{
Place = p,
CountMeet = g.Count(a => a.Meet),
CountTalk = g.Count(a => a.Talk)
};
Here's the documentation on the different joins from MSDN:
http://msdn.microsoft.com/en-us/library/bb311040.aspx