Equivalent of outer join with non-equals predicate in Linq - c#

I am looking for the Linq-equivalent of the following SQL:
SELECT t0.Fk_CompanyId, t0.CheckedUtc, t0.IsBlocking
FROM MyTable t0
LEFT OUTER JOIN MyTable t1
ON t0.Fk_CompanyId = t1.Fk_CompanyId AND t0.CheckedUtc < t1.CheckedUtc
WHERE t1.Fk_CompanyId IS NULL
AND t0.CheckedUtc IS NOT NULL
The closest I've got with Linq is:
from t0 in MyTable
join t1 in MyTable on t0.Fk_CompanyId equals t1.Fk_CompanyId into t1tmp
from t1 in t1tmp.DefaultIfEmpty()
where t1.Fk_CompanyId == null && t0.CheckedUtc != null && t0.CheckedUtc < t1.CheckedUtc
select new { cid = t0.Fk_CompanyId, cuct = t0.CheckedUtc, isbl = t0.IsBlocking }
... which produces the following SQL (reformatted slightly):
SELECT t0.Fk_CompanyId, t0.CheckedUtc, t0.IsBlocking
FROM MyTable AS t0
LEFT OUTER JOIN MyTable AS t1
ON t0.Fk_CompanyId = t1.Fk_CompanyId
WHERE (t1.Fk_CompanyId IS NULL)
AND (t0.CheckedUtc IS NOT NULL)
AND (t0.CheckedUtc < t1.CheckedUtc)
These are not exactly equivalent. (The t0.CheckedUtc < t1.CheckedUtc is pushed to the WHERE.) When I outer-left-join on t0.CheckedUtc < t1.CheckedUtc, this produces NULL values on the right-hand-side of the join. When I filter with WHERE instead, this removes all the NULL values that I am interested in.
Perspective: I am trying to find the rows in MyTable with the most recent CheckedUtc, if there are any non-NULL ones, grouped by Fk_CompanyId. And I want one row for each Fk_CompanyId. There are several "possible duplicates" that only deal with finding the most recent CheckedUtcs (but not their respective rows), or assume that CheckedUtc is NOT NULL.
So: How do I perform the equivalent of a join on a non-equals predicate in Linq?

Try this
from t0 in MyTable
From t1 in MyTable( x=>x.Fk_CompanyId=t0.Fk_CompanyId && x.CheckedUtc > t0.CheckedUtc ).DefaultIfEmpty()
where t1.Fk_CompanyId == null && t0.CheckedUtc != null
select new { cid = t0.Fk_CompanyId, cuct = t0.CheckedUtc, isbl = t0.IsBlocking }

Related

How to prevent Entity Framework from adding ISNULL checks when joining on a nullable property with LINQ

I have a Linq query that is joining on 2 nullable properties.
var result = (from A in context.TableA
join B in context.TableB
on A.ExecutionId equals B.ExecutionId
where B.InsertedBy == userName
Both rep.ExecutionId and sch.ExecutionId are nullable int in the database. What I really want Entity Framework to generate is
select
column1
from TableA A
inner join TableB B on A.ExecutionId = B.ExecutionId
where B.InsertedBy = #username
but what I get is
select
column1
from TableA A
inner join TableB B on A.ExecutionId = B.ExecutionId
OR ((A.[ExecutionId] IS NULL) AND (B.[ExecutionId] IS NULL))
where B.InsertedBy = #username
(Neither one of the ExecutionIds are primary keys). The second query gives me WAY more records than what I need, but more importantly, is not the query that I want. How can I write the LINQ so that it produces the first query or an equivalent?
If you do not want any records where ExecutionID is null, then use a filter
var result = (from A in context.TableA.Where( a => a.ExecutionID != null )
join B in context.TableB.Where( b => b.ExecutionId != null )
on A.ExecutionId equals B.ExecutionId
where B.InsertedBy == userName
...`enter code here`

LINQ - select statement in the selected column

i am intend to convert the following query into linQ
SELECT TOP 100 S.TxID,
ToEmail,
[Subject],
ProcessedDate,
[Status] = (CASE WHEN EXISTS (SELECT TxID FROM TxBounceTracking
WHERE TxID = S.TxID)
THEN 'Bounced'
WHEN EXISTS (SELECT TxID FROM TxOpenTracking
WHERE TxID = S.TxID)
THEN 'Opened'
ELSE 'Sent' END)
FROM TxSubmissions S
WHERE S.UserID = #UserID
AND ProcessedDate BETWEEN #StartDate AND #EndDate
ORDER BY ProcessedDate DESC
The following code is the linq that i converted.
v = (from a in dc.TxSubmissions
where a.ProcessedDate >= datefrom && a.ProcessedDate <= dateto && a.UserID == userId
let bounce = (from up in dc.TxBounceTrackings where up.TxID == a.TxID select up)
let track = (from up in dc.TxOpenTrackings where up.TxID == a.TxID select up)
select new { a.TxID, a.ToEmail, a.Subject,
Status = bounce.Count() > 0 ? "Bounced" : track.Count() > 0 ? "Opened" : "Sent",
a.ProcessedDate });
However this linq is too slow because the bounce and track table, how should i change the linq query to select one row only to match the SQL query above >>
SELECT TxID FROM TxOpenTracking WHERE TxID = S.TxID
in my selected column, so it can execute faster.
Note that the record contained one million records, thats why it lag
As you don't care about readability because you will end up generating the query via EF you can try to join with those two tables. (it looks that TxID is a FK or a PK/FK)
More about JOIN vs SUB-QUERY here: Join vs. sub-query
Basically your SQL looks like this:
SELECT TOP 100 S.TxID, ToEmail, [Subject], ProcessedDate,
[Status] = (CASE WHEN BT.TxID IS NOT NULL
THEN 'Bounced'
WHEN OP.TxID IS NOT NULL
THEN 'Opened'
ELSE 'Sent' END)
FROM TxSubmissions S
LEFT JOIN TxBounceTracking BT ON S.TxID = BT.TxID
LEFT JOIN TxOpenTracking OP ON S.TxID = OP.TxID
WHERE S.UserID = #UserID
AND ProcessedDate BETWEEN #StartDate AND #EndDate
ORDER BY ProcessedDate DESC
And then, you can try to convert it to LINQ something like:
v = (from subs in dc.TxSubmissions.Where(sub => sub.ProcessedDate >= datefrom && sub.ProcessedDate <= dateto && sub.UserID == userId)
from bts in dc.TxBounceTrackings.Where(bt => bt.TxID == subs.TxID).DefaultIfEmpty()
from ots in dc.TxOpenTrackings.Where(ot => ot.TxID == subs.TxID).DefaultIfEmpty()
select new { });
More about left join in linq here: LEFT JOIN in LINQ to entities?
Also if you remove default if empty you'll get a inner join.
Also you need to take a look at generated SQL in both cases.

SQL to LINQ - left join from same table using values equal to and greater than

I have the following SQL query which I am trying to convert to LINQ.
SELECT t1.*
FROM table1 t1
LEFT JOIN table1 t2
ON (t1.MusicId = t2.MusicId AND t1.MusicDetailId > t2.MusicDetailId)
WHERE t2.MusicDetailId IS NULL and t1.SingerId = 2
ORDER BY t1.MusicId
I have tried the following but I am not getting the correct data back.
var query =
from t1 in table1
from t2 in table1
where t1.MusicId == t2.MusicId && t1.MusicDetailId > t2.MusicDetailId
where t1.SingerId == 2 && t2.MusicDetailId == null
orderby t1.MusicId
select t1;
Is anyone able to help to get this SQL query converted to LINQ correctly?
var query = from t1 in table1.Where(X=> X.SingerId == 2)
join t2 in table1.Where(X=>X.MusicDetailId ==null) on t1.MusicId equals t2.MusicId
where t1.MusicDetailId > t2.MusicDetailId
select t1 ;

Nhibernate Linq is null on left join

Is there an easy way to do the following Nhibernate Linq statement
var query = from r in myTable.Query<MyTable>()
where r.Child == null
select r
The linq query above produces something similar to
SELECT MyTable.Id FROM MyTable WHERE MyTable.ChildId is null
it doesn't reference the child table and check if the left join is null like the following
SELECT MyTable.Id FROM MyTable
LEFT JOIN ChildTable ON MyTable.ChildId = ChildTable.Id
WHERE ChildTable.Id is null
var query = from r in myTable.Query<MyTable>()
where r.Child.Id == null
select r

Problem with decimal precision in SQL using Linq to SQL

I have a simple query:
var results = from k in db.tree_nodes
join s in db.stocks
on k.tree_nodes_id equals s.tree_nodes_id
into tmpTable
from rowtmp in tmpTable.DefaultIfEmpty()
select new
{
stock = (rowtmp.amount == null) ?
((k.code == null) ? (decimal?)null : (decimal?)0)
:
rowtmp.amount - rowtmp.amount_in_use,
};
This is the generated SQL code:
SELECT
(CASE
WHEN ([t1].[amount]) IS NULL THEN
(CASE
WHEN [t0].[code] IS NULL THEN CONVERT(Decimal(33,4),NULL)
ELSE CONVERT(Decimal(33,4),0)
END)
ELSE CONVERT(Decimal(33,4),[t1].[amount] - [t1].[amount_in_use])
END) AS [stock]
FROM [dbo].[tree_nodes] AS [t0]
LEFT OUTER JOIN [dbo].[stocks] AS [t1] ON [t0].[tree_nodes_id] = [t1].[tree_nodes_id]
The problem is, the generator created Decimal(33,4) when converting the results. So I'm getting "123.4560" in results instead of "123.456" All of my fields in this query are decimal(14,3). I don't mind the 33 part but I need to change the ,4 to ,3. How can I do this?
You could round the decimal values to 3 decimals?
var results = from k in db.tree_nodes
join s in db.stocks
on k.tree_nodes_id equals s.tree_nodes_id
into tmpTable
from rowtmp in tmpTable.DefaultIfEmpty()
select new
{
stock = (rowtmp.amount == null) ?
((k.code == null) ? (decimal?)null : (decimal?)0)
:
decimal.Round(rowtmp.amount,3) - decimal.Round(rowtmp.amount_in_use == null ? 0 : rowtmp.amount_in_use,3),
};
Dunno of any way to prevent linq-to-sql from type conversion.

Categories

Resources