How to do a SQL "Where Exists" in LINQ to Entities? - c#

I really want to do something like this:
Select *
from A join B on A.key = B.key join C on B.key = C.key -- propagated keys
where exists (select null from B where A.key = B.key and B.Name = "Joe") and
exists (select null from C where B.key = C.key and C.Name = "Kim")
What would the linq statement look like using Entity Framework 4 and C#?
Update:
Apparently .Contains() will produce "Where Exists" results. So, another attempt (I don't know if this will even compile LOL):
var inner1 = from recordB in B
where recordB.Name = "Joe"
select recordB.key;
var inner2 = from recordC in C
where recordC.Name = "Kim"
select recordC.key;
var result = from recordA in A
where inner1.Contains( recordA.key) &&
inner2.Contains( recordA.key)
select recordA;
EDIT: WOW this is what actually worked:
var result = from A in Products
where A.kfield1 == 1 && A.kfield2 == 2 &&
( from B in Btable
where B.otherid == "Joe" && // field I want to select by
B.kfield1 == A.kfield1 &&
B.kfield2 == A.kfield2 // Can keep adding keys here
select A.identifier // unique identity field
).Contains(A.identifier) &&
( from C in Ctable
where C.otherid == "Kim" && // field I want to select by
C.kfield1 == A.kfield1 &&
C.kfield2 == A.kfield2 // Can keep adding keys here
select A.identifier // unique identity field
).Contains(A.identifier)
select A;
This produced this SQL:
SELECT [t0].[identifier], [t0].*
FROM [A] AS [t0]
WHERE ([t0].[kfield1] = #p0) AND ([t0].[kfield2] = #p1) AND (EXISTS(
SELECT NULL AS [EMPTY]
FROM [B] AS [t1]
WHERE ([t0].[identifier] = [t0].[identifier]) AND ([t1].[otherid] = #p2) AND
([t1].[kfield1] = [t0].[kfield1]) AND
([t1].[kfield2] = [t0].[kfield2]))) AND (EXISTS(
SELECT NULL AS [EMPTY]
FROM [C] AS [t2]
WHERE ([t0].[identifier] = [t0].[identifier]) AND ([t2].[otherid] = #p3) AND
([t2].[kfield1] = [t0].[kfield1]) AND
([t2].[kfiekd2] = [t0].[kfield2]) ))
Which is what I wanted. Notice the [t0].[identifier] = [t0].[identifier], which filters out null values because null doesn't compare equal to anything including itself (in SQL)

The .Any() extension method typically maps to exists.

Have you tried adding your exists conditioning to your joins?
from a in context.AEntity
Join B in context.BEntity on A.Key equals B.Key && B.Name == "Joe"
Join C in context.CEntity on B.Key equals C.Key && C.Name == "Kim";
Not sure if that will work, but worth a shot.

Related

Define nested derived table in Entity Framework

I am converting SQL query into linq ef 6.0. I have a problem when convert nest derived table into linq query. Following is SQL query and Linq expression where i stuck
Select * FROM orders ords WITH ( NOLOCK )
INNER JOIN orders_list ol WITH ( NOLOCK ) ON ords.order_id = ol.order_id
--AND ol.order_list_id = #order_list_id
INNER JOIN product WITH ( NOLOCK ) ON product.id = ol.product_id
INNER JOIN OrderContact ca WITH ( NOLOCK ) ON ca.id= ords.OrdercontactRecipientId --and ca.is_primary_address = 1
INNER JOIN OrderContact ca1 WITH ( NOLOCK ) ON ca1.id=ords.OrderContactCustomerID--and ca.is_primary_address = 1
LEFT JOIN ( SELECT oslTemp.order_list_id ,
status_id
FROM Ticket_status_log
INNER JOIN ( SELECT osl.order_list_id ,
MAX(log_id) AS log_id
FROM Ticket_status_log osl
INNER JOIN orders_list
WITH ( NOLOCK ) ON orders_list.order_list_id = osl.order_list_id
WHERE osl.is_deleted = 0
AND osl.status_id > 0
AND osl.status_id < 3
GROUP BY osl.order_list_id
) AS oslTemp ON oslTemp.log_id = Ticket_status_log.log_id
) status_log ON ol.order_list_id = status_log.order_list_id
WHERE ords.is_deleted = 0
Following is linq expression
var query = (from ords in con.ordersDB
join ol in con.Orders_ListDB on ords.order_id equals ol.order_id
join product in con.ProductDB on ol.product_id equals product.ID
join ca in con.OrderContactDB on ords.OrderContactRecipientID equals ca.ID
join ca1 in con.OrderContactDB on ords.OrderContactCustomerID equals ca1.ID
join tktlog in con.ticket_status_logDB
)
I have stuck when there is derived nest table join. How to handle this
You can use let clause to solve this problem Microsoft Docs.
Write you subquery in let and then join the let variable with other table variables
I had figure it out by myself. I am posting the answer so that any other will get the solution
var innerQuery2 = (from tktlog in con.ticket_status_logDB
join ordlist in con.Orders_ListDB on tktlog.order_list_id equals ordlist.order_list_id
where tktlog.is_deleted == false && tktlog.status_id > 0 && tktlog.status_id < 3
group tktlog by tktlog.order_list_id into grouped
select new
{
order_list_id = grouped.Key,
log_id = grouped.Select(x => x.log_id).Max()
});
var innerQuery1 = (from tktlog in con.ticket_status_logDB
join osltemp in innerQuery2 on tktlog.log_id equals osltemp.log_id
select new
{
osltemp.order_list_id,
tktlog.status_id
});
var query = (from ords in con.ordersDB
join ol in con.Orders_ListDB on ords.order_id equals ol.order_id
join product in con.ProductDB on ol.product_id equals product.ID
join ca in con.OrderContactDB on ords.OrderContactRecipientID equals ca.ID
join ca1 in con.OrderContactDB on ords.OrderContactCustomerID equals ca1.ID
join cityml in con.CityMLDB on ca.CityMLID equals cityml.ID into leftcityml
from citymlresult in leftcityml.DefaultIfEmpty()
join state in con.StateDB on ca.StateID equals state.ID into leftstate
from stateresult in leftstate.DefaultIfEmpty()
join area in con.AreaMLDB on ca.AreaMLID equals area.ID into leftarea
from arearesult in leftarea.DefaultIfEmpty()
join statuslog in innerQuery1 on ol.order_list_id equals statuslog.order_list_id into leftquery1
from status_log in leftquery1.DefaultIfEmpty()
where ords.is_deleted == false && ords.is_voided == false && ords.type_id == 2
&& (ol.item_number ?? 0) > 0 &&
(
(
(ol.parent_id == parintid || ol.parent_id == orderListId || ol.order_list_id == orderListId)
&& (ol.SubType ?? 0) == 4
)
|| (ol.order_list_id == orderListId)
)
orderby arearesult.Name ?? ca.AreaOther
select new {});

Is it possible to add Multiple 'on' in 1 join statement of Linq

I don't know it its possible to have a nested or 2 or more on c.blah equals b.blah in one join statement in LINQ bacause i have to join a table without affecting the number of data
because if I add it to where theres a decrease of number of data and it acts as filter
What I tried as of the moment is adding && clause but it's not working
var Pos = (from a in db.Position
join b in db.Position_Location
on a.ID equals b.PositionId
join c in db.Customer
on a.CustomerID equals c.ID
join d in db.Customer_Location
on b.LocationId equals d.ID
join f in db.Worker
on userIdNew equals f.userId
join e in db.Worker_Customer_Apply_Shift <----Planning to add new validation here
on a.ID equals e.Client_Customer_PositionID into trial
from newtrial in trial.DefaultIfEmpty()
where
b.LogicalDelete == false
&& a.LogicalDelete == false
&& c.LogicalDelete == false
&& d.LogicalDelete == false
select new
{
a.ID,
Client_CustomerID = c.ID,
LogicalDelete =(newtrial == null ? true : newtrial.LogicalDelete),
}).Distinct().ToList();
Thanks in Advance :)
You could use an anonymous type with the fields you want use in your join condition:
on new { a.One, a.Two } equals new { b.One, b.Two }
If the columns in both tables don't have the same names, you need to provide names for the properties of the anonymous type:
on new { Col1 = a.One, Col2 = a.Two } equals new { Col1 = b.Three, Col2 = b.Four }

LINQ to Entities Join on Nullable Field where Null Implies "Match All"

I am attempting to run the following LINQ query using Entity Framework 5:
int taskId = 2;
query = from a in Table_A
where a.StatusCode != "DONE"
&& a.Inbound
join b in Table_B
on a.Id equals b.Id_Table_A
join c in Table_C
on a.State equals (c.State ?? a.State)
where 2 == c.Id_Task
&& b.DataType == c.DataType
select a.Id;
The line that is causing me problems is:
on a.State equals (c.State ?? a.State)
The "State" field in Table_C is nullable... and when it is null, it is used to imply "all states". As such, when "c.State" is null I want the record to be matched. If I were to write this in SQL, I would use the following:
JOIN Table_C ON Table_A.State = ISNULL(Table_C.State, Table_A.State)
Unfortunately, I am being given the following error:
The name 'a' is not in scope on the right side of 'equals'. Consider swapping the expressions on either side of 'equals'.
I will be grateful to anybody who can let me in on the secret to getting this working.
Thanks.
You can modify your code like:
int taskId = 2;
query = from a in Table_A
where a.StatusCode != "DONE"
&& a.Inbound
join b in Table_B
on a.Id equals b.Id_Table_A
from c in Table_C
where 2 == c.Id_Task
&& b.DataType == c.DataType
&& (c.State == null || a.State.Equals(c.State))
select a.Id;
A join is essentially a where clause, so here we can use the where clause due to the restrictions with join.
I managed to get this to work by moving the "DataType" check from the WHERE to the JOIN, and moving the "State" check from the JOIN to the WHERE. The resulting code that worked as I expected is as follows:
query = from a in Table_A
where a.StatusCode != "DONE"
&& a.Inbound
join b in Table_B
on a.Id equals b.Id_Table_A
join c in Table_C
on b.DataType equals c.DataType
where 2 == c.Id_Task
&& (c.State ?? a.State) == a.State
select a.Id;
Many thanks to everybody who has taken a look at this for me. :)
You can use "from" syntax instead of "join"
from a in TableA
from b in TableB
.Where(x => (x.Buy ?? a.Buy) == a.Buy
&& (x.Parity ?? a.Parity) == a.Parity)

Optimising LINQ-to-SQL queries

I have a very heavy LINQ-to-SQL query, which does a number of joins onto different tables to return an anonymous type. The problem is, if the amount of rows returned is fairly large (> 200), then the query becomes awfully slow and ends up timing out. I know I can increase the data context timeout setting, but that's a last resort.
I'm just wondering if my query would work better if I split it up, and do my comparisons as LINQ-to-Objects queries so I can possibly even use PLINQ to maximise the the processing power. But I'm that's a foreign concept to me, and I can't get my head around on how I would split it up. Can anyone offer any advice? I'm not asking for the code to be written for me, just some general guidance on how I could improve this would be great.
Note I've ensured the database has all the correct keys that I'm joining on, and I've ensured these keys are up to date.
The query is below:
var cons = (from c in dc.Consignments
join p in dc.PODs on c.IntConNo equals p.Consignment into pg
join d in dc.Depots on c.DeliveryDepot equals d.Letter
join sl in dc.Accounts on c.Customer equals sl.LegacyID
join ss in dc.Accounts on sl.InvoiceAccount equals ss.LegacyID
join su in dc.Accounts on c.Subcontractor equals su.Name into sug
join sub in dc.Accountsubbies on ss.ID equals sub.AccountID into subg
where (sug.FirstOrDefault() == null
|| sug.FirstOrDefault().Customer == false)
select new
{
ID = c.ID,
IntConNo = c.IntConNo,
LegacyID = c.LegacyID,
PODs = pg.DefaultIfEmpty(),
TripNumber = c.TripNumber,
DropSequence = c.DropSequence,
TripDate = c.TripDate,
Depot = d.Name,
CustomerName = c.Customer,
CustomerReference = c.CustomerReference,
DeliveryName = c.DeliveryName,
DeliveryTown = c.DeliveryTown,
DeliveryPostcode = c.DeliveryPostcode,
VehicleText = c.VehicleReg + c.Subcontractor,
SubbieID = sug.DefaultIfEmpty().FirstOrDefault().ID.ToString(),
SubbieList = subg.DefaultIfEmpty(),
ScanType = ss.PODScanning == null ? 0 : ss.PODScanning
});
Here's the generated SQL as requested:
{SELECT [t0].[ID], [t0].[IntConNo], [t0].[LegacyID], [t6].[test], [t6].[ID] AS [ID2], [t6].[Consignment], [t6].[Status], [t6].[NTConsignment], [t6].[CustomerRef], [t6].[Timestamp], [t6].[SignedBy], [t6].[Clause], [t6].[BarcodeNumber], [t6].[MainRef], [t6].[Notes], [t6].[ConsignmentRef], [t6].[PODedBy], (
SELECT COUNT(*)
FROM (
SELECT NULL AS [EMPTY]
) AS [t10]
LEFT OUTER JOIN (
SELECT NULL AS [EMPTY]
FROM [dbo].[PODs] AS [t11]
WHERE [t0].[IntConNo] = [t11].[Consignment]
) AS [t12] ON 1=1
) AS [value], [t0].[TripNumber], [t0].[DropSequence], [t0].[TripDate], [t1].[Name] AS [Depot], [t0].[Customer] AS [CustomerName], [t0].[CustomerReference], [t0].[DeliveryName], [t0].[DeliveryTown], [t0].[DeliveryPostcode], [t0].[VehicleReg] + [t0].[Subcontractor] AS [VehicleText], CONVERT(NVarChar,(
SELECT [t16].[ID]
FROM (
SELECT TOP (1) [t15].[ID]
FROM (
SELECT NULL AS [EMPTY]
) AS [t13]
LEFT OUTER JOIN (
SELECT [t14].[ID]
FROM [dbo].[Account] AS [t14]
WHERE [t0].[Subcontractor] = [t14].[Name]
) AS [t15] ON 1=1
ORDER BY [t15].[ID]
) AS [t16]
)) AS [SubbieID],
(CASE
WHEN [t3].[PODScanning] IS NULL THEN #p0
ELSE [t3].[PODScanning]
END) AS [ScanType], [t3].[ID] AS [ID3]
FROM [dbo].[Consignments] AS [t0]
INNER JOIN [dbo].[Depots] AS [t1] ON [t0].[DeliveryDepot] = [t1].[Letter]
INNER JOIN [dbo].[Account] AS [t2] ON [t0].[Customer] = [t2].[LegacyID]
INNER JOIN [dbo].[Account] AS [t3] ON [t2].[InvoiceAccount] = [t3].[LegacyID]
LEFT OUTER JOIN ((
SELECT NULL AS [EMPTY]
) AS [t4]
LEFT OUTER JOIN (
SELECT 1 AS [test], [t5].[ID], [t5].[Consignment], [t5].[Status], [t5].[NTConsignment], [t5].[CustomerRef], [t5].[Timestamp], [t5].[SignedBy], [t5].[Clause], [t5].[BarcodeNumber], [t5].[MainRef], [t5].[Notes], [t5].[ConsignmentRef], [t5].[PODedBy]
FROM [dbo].[PODs] AS [t5]
) AS [t6] ON 1=1 ) ON [t0].[IntConNo] = [t6].[Consignment]
WHERE ((NOT (EXISTS(
SELECT TOP (1) NULL AS [EMPTY]
FROM [dbo].[Account] AS [t7]
WHERE [t0].[Subcontractor] = [t7].[Name]
ORDER BY [t7].[ID]
))) OR (NOT (((
SELECT [t9].[Customer]
FROM (
SELECT TOP (1) [t8].[Customer]
FROM [dbo].[Account] AS [t8]
WHERE [t0].[Subcontractor] = [t8].[Name]
ORDER BY [t8].[ID]
) AS [t9]
)) = 1))) AND ([t2].[Customer] = 1) AND ([t3].[Customer] = 1)
ORDER BY [t0].[ID], [t1].[ID], [t2].[ID], [t3].[ID], [t6].[ID]
}
Try moving the subcontractor join up higher and push the where clause along with it. That way you're not unnecessarily making joins which would fail at the end.
I would also modify the select for the subcontractor id, so you don't get the Id of a potentially null value.
var cons = (from c in dc.Consignments
join su in dc.Accounts on c.Subcontractor equals su.Name into sug
where (sug.FirstOrDefault() == null || sug.FirstOrDefault().Customer == false)
join p in dc.PODs on c.IntConNo equals p.Consignment into pg
join d in dc.Depots on c.DeliveryDepot equals d.Letter
join sl in dc.Accounts on c.Customer equals sl.LegacyID
join ss in dc.Accounts on sl.InvoiceAccount equals ss.LegacyID
join sub in dc.Accountsubbies on ss.ID equals sub.AccountID into subg
let firstSubContractor = sug.DefaultIfEmpty().FirstOrDefault()
select new
{
ID = c.ID,
IntConNo = c.IntConNo,
LegacyID = c.LegacyID,
PODs = pg.DefaultIfEmpty(),
TripNumber = c.TripNumber,
DropSequence = c.DropSequence,
TripDate = c.TripDate,
Depot = d.Name,
CustomerName = c.Customer,
CustomerReference = c.CustomerReference,
DeliveryName = c.DeliveryName,
DeliveryTown = c.DeliveryTown,
DeliveryPostcode = c.DeliveryPostcode,
VehicleText = c.VehicleReg + c.Subcontractor,
SubbieID = firstSubContractor == null ? "" : firstSubContractor.ID.ToString(),
SubbieList = subg.DefaultIfEmpty(),
ScanType = ss.PODScanning == null ? 0 : ss.PODScanning
});

Linq outer join using inequality?

In SQL I'd say:
select a.*
from TableA a
left join TableB b on a.Type = b.Type and a.SomeDate < b.AnotherDate
where b.ID is null
This would select all records in TableA where no record exists in TableB of the same Type and later date.
In Linq, how do you do this?
from a in TableA
join b in TableB on a.Type equals b.Type into j // what about the comparator?
from x in j.DefaultIfEmpty()
where x == null
select a;
Thanks!
EDIT:
A few good answers have been proposed, all of which address the specific need expressed in this question, but they're all basically workarounds. They all translate to a nested "exists" queries in one way or another, whereas the SQL in the question is one neat query without any nesting. The case given here is just an example of a general principle; what I'd really like to see is a Linq expression that will translate to (roughly) the syntax of the above SQL query.
Something like this ought to help:
var results =
(from itemA in TableA
from itemB in TableB
where itemA.Type != itemB.Type && itemA.Date < itemB.Date
select itemA).Distinct();
from a in tableA
let rights =
from b in tableB
where a.Type == b.Type && a.Date < b.Date
select b
where !rights.Any()
select a;
It's translated into:
SELECT [t0].[Type] AS [Type], [t0].[SomeDate] AS [SomeDate]
FROM [TableA] AS [t0]
WHERE NOT (EXISTS(
SELECT NULL AS [EMPTY]
FROM [TableB] AS [t1]
WHERE ([t0].[Type] = [t1].[Type]) AND ([t0].[SomeDate] < [t1].[AnotherDate])))
var results = TableA.Where(a =>
!TableB.Any(b => a.Type == b.Type && a.Date < b.Date))
If you want the linq query to be exactly as your SQL you can write:
var result = from a in TableA
from b in TableB.Where(b => a.Type = b.Type && a.SomeDate < b.AnotherDate).DefaultIfEmpty()
where b == null
select a;
But I would say that the first solution is better as the where b == null would result in a filter operation in the queryplan.

Categories