Aggregate of DateTime split in Date and Time columns - c#

I have the following query
from booking in query
join ba in Context.BookingAddresses on booking.Id equals ba.BookingId into collections
let firstCollection = (from d in collections where d.AddressType == BookingAddressType.Collection select d.RequestedDate).Min()
where
EntityFunctions.TruncateTime(queryArgs.DateFrom.Value) <= EntityFunctions.TruncateTime(firstCollection) &&
EntityFunctions.TruncateTime(queryArgs.DateTo.Value) >= EntityFunctions.TruncateTime(firstCollection)
select booking;
In the let clause I would actually need the Min of a merged DateOnly and TimeSpan value
d.RequestedDate [DateOnly] + d.RequestedDateTimeFrom [TimeSpan] that look like this in the DB:
RequestedDate : 2013-06-01
RequestedDateTimeFrom : 13:50
This does not compile:
let firstCollection = (from d in collections where d.AddressType == BookingAddressType.Collection select d.RequestedDate + d.RequestedDateTimeFrom)
Edit: In the meantime I thought of a different approach, that would actually solve my main issue, namely, that if there are more datetimes that have both the date and time values the same, I compare them by a thrid Sequence column. So it boils down to simple sorting as this:
from booking in query
join ba in Context.BookingAddresses on booking.Id equals ba.BookingId into collections
let firstCollection = collections.OrderBy(c => c.RequestedDate).ThenBy(c => c.RequestedFromTime).ThenBy(c => c.Sequence).FirstOrDefault()
//(from d in collections where d.AddressType == BookingAddressType.Collection select d.RequestedDate).Min()
where
EntityFunctions.TruncateTime(queryArgs.DateFrom.Value) <= EntityFunctions.TruncateTime(firstCollection.RequestedDate) &&
EntityFunctions.TruncateTime(queryArgs.DateTo.Value) >= EntityFunctions.TruncateTime(firstCollection.RequestedDate)
select booking;

This is maybe not the most elegant way (and untested, to be honest), but I would try an imbricated SqlFunctions.DateAdd with SqlFunctions.DatePart on the TimeSpan
SqlFunctions.DateAdd("hh",
SqlFunctions.DatePart("hh", d.RequestedDateTimeFrom),
SqlFunctions.DateAdd("mi",
SqlFunctions.DatePart("mi", d.RequestedDateTimeFrom),
d.RequestedDate);
(You may also use EntityFunctions.AddHours and EntityFunctions.AddMinutes instead of SqlFunctions.DateAdd)

Related

Only include in where condition if date is not low date(01/01/0001)

I have a query that has a where condition to check and find addresses that were added after a certain date. The date field is not required so I want Date field in where condition to be only considered if it is not 1/1/0001.
dtmDate is the parameter that is being passed
Query
from b in _context.customer
join d in _context.Address on b.id equals d.Id
join e in _context.units on d.Id equals e.Id
where (req.dtmDate.Year != 1 && d.DateAdded >= req.dtmDate)
select new modelAddress
{
address= d.address
}
But this is not working. It is not returning any rows
I'd leverage the fact that LINQ queries are not executed when you write them, so you can add clauses conditionally after you've created a base query:
var query = from b in _context.customer
join d in _context.Address on b.id equals d.Id
join e in _context.units on d.Id equals e.Id;
if(req.dtmDate.Year != 1)
query = query.Where(d.DateAdded >= req.dtmDate);
var result = query.Select(
new modelAddress
{
address= d.address
}
);
I prefer this because I've previously run into issues, particularly with EF LINQ queries when the Where clause contains something that evaluates to true locally with in the code, rather than as something the DB will evaluate. It seems to work out better when "wildcarding" DB queries, to use a pattern of "if x is true then add-another-where-clause" rather than saying "where(local-value-of-x-equals-local-constant OR some-db-data-value-equals-y)"
If I understand you correctly, you have a DateTime object called req.dtmDate that may be set to a default value, and you want to return all items where the item's DateAdded field is greater than req.dtmDate, unless req.dtmDate is 1/1/0001, in which case all records should be returned.
If that's the case, I think you could just modify your existing code to:
where (req.dtmDate.Year == 1 || d.DateAdded >= req.dtmDate)

Linq Select items where they are before a certain date or the first after

I have a Linq-to-Entity query which grabs all the items before a certain date, I was wondering if anyone body knows a way to also grab the first record larger than that date in the same query.
context.Items.Where(x => x.CheckDate < DateTime.Now)
Is there a way to modify this so that I can grab the first date after without making two queries?
var items = from u in context.Items
let dt = (from i in context.Items
where i.CheckDate> DateTime.Now orderby i.CheckDate ).FirstOrDefault()
where u.CheckDate <=dt
select u;
You need two queries, but to merge the results you can use concat,
var result =context.Items.Where(x => x.CheckDate < DateTime.Now).Concat( context.Items.OrderByDescending(t=>t.CheckDate >Yourdatetime).FirstOrDefault());
You don't need to do all of that...
context.Items.Where(x => x.CheckDate < DateTime.Now).OrderByDescending(x => x.CheckDate).FirstOrDefault();

comparison operator not supported for type int[] - linq to sql

Here is the problematic line:
var originalSummaryCandidates =
(from a in masterDB.tbl_thirty_second_summaries_multi_variant_associations
join d in masterDB.tbl_thirty_second_summaries_multi_variants on a.ThirtySecSummaryId equals d.ThirtySecondSummaryId_this
where d.DrugId == drugId &&
variantGenotypeIds.Contains(new int[] {a.VariantId, a.GenotypeId})
select d.ThirtySecondSummaryId_this)
.Distinct()
.ToList();
variantGeotpeIds is of type List<int[]>. Both a.VariantId and a.GenotypeId are of type int.
I cannot figure out why it why it will not do the comparison. Is this a deferred execution issue? It doesn't seem like it should be...
Thanks in advance.
List<T>.Contains only takes a single parameter of type T. In your case, T is Int32 but you're passing in a Int32[].
If you want to check that both values are in the list, you have to break the calls apart:
where d.DrugId == drugId &&
variantGenotypeIds.Contains(a.VariantId) &&
variantGenotypeIds.Contains(a.GenotypeId)
EDIT
If variantGenotypeIds is actually a List<Int32[]>, then there's another issue. LINQ to SQL will try to convert your query into its SQL equivalent. In this case, there's no way to translate your query into SQL so LINQ to SQL will throw an Exception.
If you really need to query this way, you'll have to read the records into memory first and then query using LINQ to Objects (which may or may not be a big deal depending on how many rows you are reading):
var query =
from a in masterDB.tbl_thirty_second_summaries_multi_variant_associations
join d in masterDB.tbl_thirty_second_summaries_multi_variants
on a.ThirtySecSummaryId equals d.ThirtySecondSummaryId_this
where d.DrugId == drugId
select new { a, d }
var originalSummaryCandidates =
(from q in query.AsEnumerable()
where variantGenotypeIds.Contains(new [] { q.a.VariantId, q.a.GenotypeId})
select d.ThirtySecondSummaryId_this)
.Distinct()
.ToList();
Array comparison uses reference equality by default. It's possible that linq-to-sql just tries to translate that into SQL that compares the values, but you'd have to look at the generated SQL to be sure. Another option would be to use Any instead:
where d.DrugId == drugId &&
variantGenotypeIds.Any(v => v[0] == a.VariantId && v[1] == a.GenotypeId)
but I'm not sure if Linq-to-Sql will be able to translate that to the correct SQL either. Another option would be to project the List` to a > and then do a string comparison:
variantGenotypeStrings = variantGenotypeIds.Select(v => string.Format("{0}|{1}", v[0],v[1]);
var originalSummaryCandidates =
(from a in masterDB.tbl_thirty_second_summaries_multi_variant_associations
join d in masterDB.tbl_thirty_second_summaries_multi_variants on a.ThirtySecSummaryId equals d.ThirtySecondSummaryId_this
where d.DrugId == drugId &&
variantGenotypeStrings.Contains(string.Format("{0}|{1}", a.VariantId, a.GenotypeId))
select d.ThirtySecondSummaryId_this)
.Distinct()
.ToList();

SUM() in SQL to LINQ

Simple question really but it seems tricky to do:
I am trying to convert the following SQL Query to LINQ expression:
select SUM(timespan) as timespan from TimeRegistrations as tr
where Activity_Id_FK =1 and tr.date>= DATEADD(DAY,-30,GETDATE());
This is what I have so far :
var total_hours_spent = from e in DB.TimeRegistrations
where e.Activity_Id_FK ==activity_id && e.date >=date.AddDays(30)
select e.Sum(e.timespan);
The compiler complains at e.Sum(e.timespan). I don't know how to select the Sum of the timespan from the TimeRegistrations table. Any help will be greatly appreciated.
In your query e is a single TimeRegistration entity with its Activity_Id_FK, date and other fields. So, you problem is here:
e.Sum(e.timespan)
Here you have two issues - first one is calling Sum on entity (it's not queryable set of entities - there is nothing to sum in single entity). And you missed syntax of Sum method - it should accept lambda with property selector Sum(e => e.timespan).
You should select timespan values from TimeRegistrations table, and then call Sum() method:
(from e in DB.TimeRegistrations
where e.Activity_Id_FK ==activity_id && e.date >= date.AddDays(30)
select e.timespan).Sum()
SQL query will be generated only when Sum() is called, so it will end up with query you want. You also can select whole TimeRegistrations entity and then select which field to sum (same query will be generated):
(from e in DB.TimeRegistrations
where e.Activity_Id_FK ==activity_id && e.date >= date.AddDays(30)
select e).Sum(e => e.timespan)
I don't like to mix query syntax with method calls, so here is pure lambda syntax:
DB.TimeRegistrations
.Where(e => e.Activity_Id_FK == activity_id && e.date >= date.AddDays(30))
.Sum(e => e.timespan)
BTW: Small advise - choose variable names which correlate with entity they represent. E.g. instead of e I'do go with variable r or tr to represent TimeRegistration entity.
Apply sum after query like this one,
var total_hours_spent = (from e in DB.TimeRegistrations
where e.Activity_Id_FK ==activity_id && e.date >=date.AddDays(30)
select e).Sum(e => e.timespan);
Hope this will solve your issue
Use following code to find total hours spent
var total_hours_spent = (from e in DB.TimeRegistrations
where e.Activity_Id_FK ==activity_id && e.date >=date.AddDays(30)
select e).Sum(e => e.timespan.TotalHours);
Maybe a more readable syntax
var total_hours_spent = DB.TimeRegistrations
.Where(e => e.Activity_Id_FK ==activity_id &&
e.date >=date.AddDays(30))
.Sum(e => e.timespan);

LINQ SELECT with Max and Where in SUBQUERY

I'm trying to code the following SQL statement into LINQ, but struggling. Here is the SQL statement:
SELECT C.ITM_CD, C.BUY_QTY, C.COST
FROM CST C
WHERE C.eff_dt IN (SELECT MAX (D.eff_dt)
FROM CST D
WHERE D.itm_cd = C.itm_cd
AND D.eff_dt <= '22-APR-2014')
ORDER BY C.itm_cd
And here is my LINQ attempt that brings nothing back, even though EVERY eff_dt has a date less than today's date and keep in mind, in my 'real' program this date will be changing.
var results = from c in Csts
let MaxEffDate = (from d in Csts
where d.EffDt <= DateTime.Parse("04/22/2014").Date
&& d.ItmCd == c.ItmCd
select d.EffDt).Max()
where c.EffDt.Equals(MaxEffDate)
select new
{
c.ItmCd,
c.BuyQty,
c.Content
};
Lambda code would be great! So for each itm_cd row in the CST table, I want all the rows to come back that have the Max effective date for that itm_cd as long as the eff_dt <= a certain date. I hard-coded today's date so that I know every row should be coming back, but I get nothing.
I've seen a lot of sub-selects on this site but they always use the MAX function without a WHERE clause of <= for that MAX column.
I can't really test this at the moment, but something like this should get you close:
var results = Csts.Where(d =>
d.EffDt == Csts.Where(x =>
x.ItmCd == d.ItmCd &&
x.EffDt <= DateTime.Now)
.Max(x => x.EffDt))
.OrderBy(d => d.ItmCd)
.Select(d => new { d.ItmCd, d.BuyQty, d.Content });

Categories

Resources