LINQ to SQL, select targets with max date - linq

I ended up with this horrible code below, I can't get a better result now.
What is a better way of doing that?
It's about this part of my database:
EDIT
A Patient has a Subscription to multiple MonitoringObjects. Target records refer to these Subscriptions. I want to retrieve the target records with the newest date per Subscription for a given Patient and a Category of MonitoringObjects. These target records may have different max dates, as Targets can be added for Subscriptions to MonitoringsObjects independently.
var subs = db.Subscriptions.Where(p => p.PatientID == patID).Where(p => p.MonitoringObject.Category.Name == "Medication");
var targets1 = from t in db.Targets
where subs.Contains(t.Subscription)
select t;
var maxTa = from t in db.Targets
group t by t.SubscriptionID
into g
select new
{
Ky = g.Key,
Date = g.Max(p => p.Date)
};
var targets2 = from t in targets1
where maxTa.Select(p => p.Ky).Contains( t.SubscriptionID ) &&
maxTa.Select(p => p.Date).Contains( t.Date )
select t;

I am not exactly sure what this is trying to achieve, or what your datamodel looks like, but something like this?
var subs = db.Subscriptions.Where(p => p.PatientID == patID).Where(p => p.MonitoringObject.Category.Name == "Medication");
var targets = subs
.SelectMany(s => s.Targets)
.Where(t => t.Date == t.Subscription.Targets.Max(_t => _t.Date))

Related

LINQ join multi tables in Lambda ex

I hope that someone can help me out here or to point me in the right direction:
I have an issue with this script from my SQL. This is what I have from the database and it comes out with success.
Script for water counter that has reminder for active period:
Select *
from WaterCounters
join WaterUsages on WaterUsages.WaterCounterId = WaterCounters.Id
join Periodes per on per.PeriodeId=WaterUsages.PeriodeId
where per.IsApproved = 1
Now I am trying to "translate/convert" into a LINQ query:
var waterCounter = Context.MyWaterCounter.Join(xx).ToList...
I am sure that I am doing it right but please advice me in how to "join more than one table" - thanks in advance.
Best regards,
Kvolle
UPDATED:
this is my script so far:
var waterCounter = Context.MyWaterCounter.Join(
Context.MyWaterUsages
.Where(x => x.WaterCounterId ==x.WaterCounterId)
.ToList());
Here is as I see it one table.
The tables in the database look like this:
It will look something like below
var waterCounter = Context.MyWaterCounter.Join(
Context.MyWaterUsages,
(WC) => WC.WaterCounterId,
(WU) => WU.Id,
(WC, WU) => new { WaterCounter = WC, WaterUsage = WU}
)
.Join(
Context.Periodes.Where(p => p.IsApproved=1),
(WUC) => WUC.WaterUsage.PeriodeId,
(P) => P.PeriodeId,
(WUC, P) => new { WaterCounter = WUC.WaterCounter, WaterUsage = WUC.WaterUsage, Period = P}
)
.ToList());

Limiting a query to the rows created today (Linq-to-Entities, Datetime)

So I've got this query:
var query = from r in context.Cars
let h = context.CarHistories
.Where(u => r.ID == u.CarID)
.Where(u => u.EventID == intEventID)
.OrderByDescending(u => u.CreatedDate)
.FirstOrDefault()
select new RefundListItem()
{
ID = r.ID,
VendorID = r.VendorID,
RecipientName = r.RecipientName,
MostRecentSubmittedName = h.CreatedName,
CreatedDate = h.CreatedDate,
};
Later on, I add this to the query because I only want the rows that were created today:
DateTime today = DateTime.Today;
query.Where(u => Convert.ToDateTime(u.CreatedDate) >= today);
For some reason, this where statement does not affect the query at all. The query still returns items created from previous days instead of limiting them to just the rows created today.
I have also tried this but it does not work either:
DateTime today = DateTime.Today.Date;
query.Where(u => Convert.ToDateTime(u.CreatedDate.Date) >= today.Date);
I'm using Linq-to-Entities (MVC 4, EF 4).
Where does not modify query instance, it returns new one with additional condition added. Assign it back to query to make it work:
query = query.Where(u => Convert.ToDateTime(u.CreatedDate.Date) >= today.Date);

How to find all rows of items that have a part in common using LINQ?

I need to return all records (items) that has a part (X) so I can use that in a group or .GroupBy afterwards
Using this summary data:
ItemName PartName
1 A
1 B
2 A
3 C
So Item1 has two parts (A,B), etc...
I need a LINQ query that will
- find all items that have part A (i.e items 1 and 2)
- return all rows for all these items
1 A
1 B
2 A
Notice that the end result returned the row (1 B) because Item1 has PartA and so I need to get back all rows for Item1.
I was looking at something like:
let items = from data in summary where data.PartName == A select new { data.ItemName } // to get all the items I need
But then, now that I have that list I need to use it to get all the rows for all items listed, and I can't seem to figure it out ...
Actual Source Code (for reference):
NOTE:
Recipe = ITEM
Ingredient = PART
(I was just trying to make it simpler)
ViewFullRecipeGrouping = (
from data in ViewRecipeSummary
group data by data.RecipeName into recipeGroup
let fullIngredientGroups = recipeGroup.GroupBy(x => x.IngredientName)
select new ViewFullRecipe()
{
RecipeName = recipeGroup.Key,
RecipeIngredients = (
from ingredientGroup in fullIngredientGroups
select new GroupIngredient()
{
IngredientName = ingredientGroup.Key
}
).ToList(),
ViewGroupRecipes = (
from data in ViewRecipeSummary
// this is where I am looking to add the new logic to define something I can then use within the next select statement that has the right data based on the information I got earlier in this query.
let a = ViewRecipeSummary.GroupBy(x => x.RecipeName)
.Where(g => g.Any(x => x.IngredientName == recipeGroup.Key))
.Select(g => new ViewRecipe()
{
RecipeName = g.Key,
IngredientName = g.Select(x => x.IngredientName)
})
select new GroupRecipe()
{
// use the new stuff here
}).ToList(),
}).ToList();
Any help would be much appreciated.
Thanks,
I believe this does what you want:
var data = /* enumerable containing rows in your table */;
var part = "X";
var items = new HashSet<int>(data
.Where(x => x.PartName == part)
.Select(x => x.ItemName));
var query = data.Where(x => items.Contains(x.ItemName));
If I understand your comment at the end, I believe this also does what you want:
var query = data
.GroupBy(x => x.ItemName)
.Where(g => g.Any(x => x.PartName == part))
.Select(g => new
{
ItemName = g.Key,
PartNames = g.Select(x => x.PartName)
});

LINQ Max Date with group by

I have a requirement to sort a list by 1) the number of times a distinct item appears and then 2) if the count of two distinct rows is the same, the most recently used date of that group.
My group by function and sorting by count is working without the date:
(from x in data
group x by new { x.Col1, x.Col2, x.Col3}
into g
let count = g.Count()
select new
{
g.Key.Col1,
g.Key.Col2,
g.Key.Col3,
count
}).OrderByDescending(x => x.count)
However, I have been unable to successfully add the date sort. I was trying to add the date column as an aggregate in the group by expression, but that doesn't work.
(from x in data
group x by new { x.Col1, x.Col2, x.Col3, MaxDate = x.CreatedDateTime.Max()}
into g
let count = g.Count()
select new
{
g.Key.Col1,
g.Key.Col2,
g.Key.Col3,
count,
g.Key.MaxDate
}).OrderByDescending(x => x.count).ThenByDescending(x => x.MaxDate)
I get why it doesn't work, I just can't think of another route to add the secondary sort. Any ideas are appreciated!
This is what you're looking for (I think)
from x in data
group x by new { x.Col1, x.Col2, x.Col3}
into g
let count = g.Count()
select new
{
g.Key.Col1,
g.Key.Col2,
g.Key.Col3,
MaxDate = g.Select(x => x.CreatedDateTime)
.OrderByDescending(d => d).FirstOrDefault(),
count
}).OrderByDescending(x => x.count).ThenByDescending(x => x.MaxDate)
How about:
var ordered = Elev8SnaTowelTables
.GroupBy(x => new { x.Col1, x.Col2, x.Col3 })
.OrderByDescending(g => g.Count())
.ThenByDescending(g => g.Max(x => x.CreatedDateTime));

In Linq2SQL, how do I get a record plus the previous and next in the sequence in a single query?

Given a date, what is the most efficient way to query the last record before that date, any record that equals that date, and the next one after that date.
It should be functionally equivalent to a query like this:
from asset in Assets
where asset.Id == assetId
select new {
Previous = (from a in a.Orders where a.Date < myDate orderby a.Date descending select a).FirstOrDefault(),
Current = (from a in a.Orders where a.Date == myDate select a).SingleOrDefault(),
Next = (from a in a.Orders where a.Date > myDate orderby a.Date select a).FirstOrDefault()
}
As is, this query runs three queries, and presumably has to sort the dataset by myDate three times to do it.
Some similar questions:
How do I get 5 records before AND after a record with a specific ID? (just uses two queries)
How do I get records before and after given one? Not in Linq, and therefore hard for me to take advantage of (my team will get annoyed).
To provide the "most efficient" query depends on what you mean by efficient.
If you want a single query to the database, a single sort of orders by date and finally fast look-ups by date then I suggest the following might be the most efficient. :-)
var orders =
(from a in Assets
where a.Id == assetId
from o in a.Orders
orderby o.Date
select o).ToArray();
var previous = orders.LastOrDefault(o => o.Date < myDate);
var current = orders.SingleOrDefault(o => o.Date == myDate);
var next = orders.FirstOrDefault(o => o.Date > myDate);
This should query the database once for the orders associated with the required asset Id, sort them by date, and return them as an array in memory. Since this is in memory it is now blindingly fast to look for the current, previous & next records for the specified date.
Does your Orders table have a sequential ID field? If so, you might be able to do it with:
from asset in Assets
where asset.Id == assetID
let current = asset.Orders.Where(x => x.Date == myDate).FirstOrDefault()
where current != null
let previous = asset.Orders.Where(x => x.id == current.id - 1).FirstOrDefault()
let next = asset.Orders.Where(x => x.id == current.id + 1).FirstOrDefault()
select new {
Previous = previous,
Current = current,
Next = next
};
If it doesn't, then it'd be a bit more code:
from asset in Assets
where asset.Id == assetID
let current = asset.Orders.Where(x => x.Date == myDate).FirstOrDefault()
where current != null
let previous = asset.Orders.Where(x => x.Date < current.Date).OrderByDescending(x => x.Date).FirstOrDefault()
let next = asset.Orders.Where(x => x.Date > current.Date).OrderBy(x => x.Date).FirstOrDefault()
select new {
Previous = previous,
Current = current,
Next = next
};
That should get compiled into a single SQL query that utilizes sub-queries. IE: the database server will execute multiple queries, but your client program is only submitting one.
Edit One other idea that would work if your Order table had sequential IDs:
var sample = (from asset in Assets
where asset.Id == assetID
let current = asset.Orders.Where(x => x.Date == myDate).FirstOrDefault()
where current != null
from order in asset.Orders
where order.Id == current.id - 1
select order)
.Take(3)
.ToArray();
var Previous = sample[0];
var Current = sample[1];
var Next = sample[2];
Other Answers, for example, SkipWhile etc. very very slow. Good luck ^^
//Current Record
var query
= (from item in db.Employee
where item.UserName.Equals(_username)
select item).SingleOrDefault();
//Next Record
var query
= (from item in db.Employee
where item.UserName.CompareTo(_username) > 0
select item).FirstOrDefault();
//Previous Record
var query
= (from item in db.Employee
where item.UserName.CompareTo(_username) < 0
orderby item.UserName Descending
select item).FirstOrDefault();
Almost the same, but the SQL query plan might be different.
var q =
from asset in Assets
where asset.Id == assetID
select new
{
Previous = asset.Orders.where(a => a.Date == asset.Orders.Where(x => x.Date < myDate).Max(x => x.Date)).FirstOrDefault(),
Current = asset.Orders.Where(x => x.Date == myDate).FirstOrDefault(),
Next = asset.Orders.where(a => a.Date == asset.Orders.Where(x => x.Date > myDate).Min(x => x.Date)).FirstOrDefault()
};

Resources