LINQ left join generates wrong SQL query - c#

I have this LINQ query:
from sol in context.SalesOrderLines
join mpso in context.MulticlientsoParentChildsoRelations on sol.orderid equals mpso.parentsalesorderid into lmpso
from mpso in lmpso.DefaultIfEmpty()
join cso in context.SalesOrders on mpso.childsalesorderid equals cso.id into lcso
from cso in lcso.DefaultIfEmpty()
join csol in context.SalesOrderLines on cso.id equals csol.orderid into lcsol
from csol in lcsol.DefaultIfEmpty()
join pll in context.PickingLine on (csol == null ? sol.id : csol.id) equals pll.salesorderlineid
join pl in context.PickingLists on pll.pickinglistid equals pl.id
join pfi in context.PfiSubmitterQuantities on pll.id equals pfi.pickinglineid into ppfi
from pfi in ppfi.DefaultIfEmpty()
where sol.orderid == palletid && (pfi == null ? pl.wholepallets == true : true)
select new Helper { Id = pfi.palletid ?? pll.palletid, BoolValue01 = pfi == null }
And here is sixth line with left join
join csol in context.SalesOrderLines on cso.id equals csol.orderid into lcsol
from csol in lcsol.DefaultIfEmpty()
And it generates strange SQL query, here's how it looks when I do linq to sql (take a look at 7th line):
SELECT distinct
CASE WHEN (pfi."palletid" IS NULL) THEN (pll."palletid") ELSE (pfi."palletid") END AS "C1",
CASE WHEN (pfi."id" IS NULL) THEN (TRUE) ELSE (FALSE) END AS "C2"
FROM salesorderlines AS sol
LEFT OUTER JOIN multiclientso_parentchildsorelations AS mpso ON sol."orderid" = mpso."parentsalesorderid"
LEFT OUTER JOIN salesorders AS cso ON mpso."childsalesorderid" = cso."id"
LEFT OUTER JOIN salesorderlines AS csol ON cso."id" = csol."orderid" OR cso."id" IS NULL AND csol."orderid" IS NULL
INNER JOIN pickingline AS pll ON
CASE WHEN (csol."id" IS NOT NULL) THEN (csol."id") ELSE (sol."id") END = pll."salesorderlineid"
OR CASE WHEN (csol."id" IS NOT NULL) THEN (csol."id") ELSE (sol."id") END IS NULL AND pll."salesorderlineid" IS NULL
INNER JOIN pickinglist AS pl ON pll."pickinglistid" = pl."id"
LEFT OUTER JOIN pfi_submittedquantities AS pfi ON pll."id" = pfi."pickinglineid"
WHERE sol."orderid" = 24863039
AND CASE WHEN (pfi."id" IS NULL) THEN ( CASE WHEN (TRUE = pl."wholepallets") THEN (TRUE) WHEN (NOT (TRUE = pl."wholepallets" AND pl."wholepallets" IS NOT NULL)) THEN (FALSE) END ) ELSE (TRUE) END = TRUE
The problem is how does my linq left join translates from this LINQ
join csol in context.SalesOrderLines on cso.id equals csol.orderid into lcsol
from csol in lcsol.DefaultIfEmpty()
to this SQL
LEFT OUTER JOIN salesorderlines AS csol ON cso."id" = csol."orderid" OR cso."id" IS NULL AND csol."orderid" IS NULL
From where does the OR part comes?

Thanks to #ArcaArtem, I've found that SalesOrderLines table had orderid nullable int foreign key field, which in my case would never be null, so changed it to simple int. Now it generates correct sql.

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 {});

Linq query join is not working

Hi i am trying to join two tables in c#. the join code is given below. The problem is that when there is null value for tourid in tb_abc then in will not include that row from tb_abc in the list.
return (from p in context.tb_abc
from o in context.tb_Second
where o.id==p.tourId
where p.driverId == driverId
select new abcBean
{
id=p.id,
name=o.name
}).ToList<abcBean>();
Can anyone tell me what i am doing wrong
You are not doing an inner join in that query. You are doing a cross join, its where you have two tables and join each record to every other record.
If you want to include rows that return null on one of the constraints you need a left outer join.
return (from p in tb_abc
join o in tb_Second on p.tourId equals o.id into po
where p.driverId == driverId
from subpo in po.DefaultIfEmpty()
select new abcBean
{
id=p.id,
name=(subpo == null ? String.Empty : subpo.Name)
}).ToList();
Consider these two sql statements:
The first a cross join:
select id, name
from tb_abc o,
tb_Second p
where
o.id = p.tourID
and p.driverID = #driverID
The second a left outer join:
select id, name
from tb_abc o
LEFT OUTER JOIN tb_Second p on o.id = p.tourID
where
p.driverId = #driverID
The second will give you one set of the records, that include the null value of o.id.
The first will give you something of a Cartesian product which you rarely want.
Linq's DefaultIfEmpty() puts the default value (null) into the record if it doesnt find a match for the one side, so it behaves like the left outer join.
you can use left outer join like
return (from p in context.tb_abc
join o in context.tb_Second on o.id==p.tourId into gt
where p.driverId == driverId
from subsecond in gt.DefaultIfEmpty()
select new abcBean
{
id=p.id,
name=(subsecond == null ? String.Empty : subsecond.Name)
}).ToList<abcBean>();

Avanced Entity Framework Query Example

this my sql command.
select c.nombrecompleto fullname,c.direccion street ,c.telefono phonenumber,
c.limitecredito creditlimit,sum(vd.preciofinal) balance , case when max(a.fechacreacion) is null then '' else max(a.fechacreacion) end
lastpay
,
case when u.nombre is null then '' else u.nombre end paidTo
from cliente c
inner join venta v on c.idcliente=v.idcliente
inner join ventadetalle vd on v.idventa=vd.idventa
inner join prestamo p on p.idventa=v.idventa
left join abono a on c.idcliente=a.idcliente
left join usuario u on a.creadopor=u.idusuario
where
c.estatus=1 and
v.estatus=1 and
vd.estatus=1 and
p.estatus=1 and
(a.estatus is null or a.estatus=1)
and (u.estatus=1 or u.estatus is null)
group by c.nombrecompleto,c.direccion,c.telefono,c.limitecredito
,u.nombre
having sum(vd.preciofinal)>0
i tried converted it to entity framework but i could not.
this i got (i did not finish)
from c in Clientes
join v in Ventas on c.Idcliente equals v.Idcliente
join vd in Ventadetalles on v.Idventa equals vd.Idventa
join p in Prestamos on v.Idventa equals p.Idventa
join a in Abonos on c.Idcliente equals a.Idcliente into al
from a in al.DefaultIfEmpty()
join u in Usuarios on a.Creadopor equals u.Idusuario into ul
from u in ul.DefaultIfEmpty()
where
c.Estatus==1 &&
v.Estatus==1 &&
vd.Estatus==1 &&
p.Estatus==1 &&
(a.Estatus==1 || a.Estatus== null) &&
(u.Estatus==1 || u.Estatus== null)
group new{c.Idcliente,vd,a} by new { c.Nombrecompleto, c.Direccion, c.Telefono, c.Limitecredito,
usuarionombre=((u.Nombre!=null)?u.Nombre:u.Nombre)
}
not sure but i believe i have got
from c in Clientes
join v in Ventas on c.Idcliente equals v.Idcliente
join vd in Ventadetalles on v.Idventa equals vd.Idventa
join p in Prestamos on v.Idventa equals p.Idventa
join a in Abonos on c.Idcliente equals a.Idcliente into al /*this line*/
from a in al.DefaultIfEmpty() /*and this is for to do left outer join or left join*/
join u in Usuarios on a.Creadopor equals u.Idusuario into ul
from u in ul.DefaultIfEmpty() /*so you see here other left join*/
where
c.Estatus==1 &&
v.Estatus==1 &&
vd.Estatus==1 &&
p.Estatus==1 &&
(a.Estatus==1 || a.Estatus== null) &&
(u.Estatus==1 || u.Estatus== null) /*this is for to do (a.estatus is null or a.estatus=1)*/
group new
{
vd.Preciofinal,a.Fechacreacion /*these are the max or sum or min columns */}
by new {c.Idcliente, c.Nombrecompleto, c.Direccion, c.Telefono, c.Limitecredito,
usuarionombre=((u.Nombre!=null)?u.Nombre:u.Nombre/*the group by part in a normal sql command */
)
}
into agrupacion /*inside a new table */
where agrupacion.Sum(x=> x.Preciofinal)>0 /*this would be a having sum in a normal sql command */
select new {
/*my columns i needs only and the sum and max*/
agrupacion.Key.Idcliente,agrupacion.Key.Nombrecompleto,agrupacion.Key.Direccion,agrupacion.Key.Telefono,
agrupacion.Key.Limitecredito,agrupacion.Key.usuarionombre, saldo=agrupacion.Sum(x=> x.Preciofinal)
,ultimopago=((agrupacion.Max(x=> x.Fechacreacion)!=null)?agrupacion.Max(x=> x.Fechacreacion):DateTime.Now /*ultimopago would be a case when max(fechacreacion )is null then getdate() else max(fechaultimopago)* end as ultimopago/)
}
i hope this could help to other

How to use AND operator in LINQ Join

I have following SQL Query
SELECT * FROM KDMS_dynamic vd
INNER JOIN KDMS_definition tblKDMS ON tblKDMS.SystemID=vd.SystemID
LEFT OUTER JOIN KDMS_typeid tblKDMSType ON tblKDMS.TypeId=tblKDMSType.TypeId
INNER JOIN KDMS_configuration tblKDMSConfig ON tblKDMS.SystemID=tblKDMSConfig.SystemID
AND tblKDMSConfig.ConfigurationDate = (SELECT MAX(ConfigurationDate)
FROM KDMS_configuration vc
WHERE vc.SystemID=tblKDMSConfig.SystemID)
AND vd.LastUpdated=(SELECT MAX(LastUpdated) FROM KDMS_dynamic vd
WHERE vd.SystemID=tblKDMS.SystemID)
WHERE
DeletionDate IS NULL
AND LongDescription IS NOT NULL
AND tblKDMS.TypeId <> 1
As I have try convert the same in to LINQ but I can not due to AND OPERATOR use in Inner Join.
I am not aware how to use And Operator in LINQ , JOIN
As folloing the linq code which i try.
IQueryable<getKDMS> query = (from VD in this._db.GetTable<KDMS_dynamic>()
join TblKDMS in this._db.GetTable<KDMS_definition>() on VD.SystemID equals TblKDMS.SystemID
join TblKDMSType in this._db.GetTable<KDMS_typeid>().DefaultIfEmpty() on TblKDMS.TypeID equals TblKDMSType.TypeID
join TblKDMSConfig in this._db.GetTable<KDMS_configuration>() on TblKDMS.SystemID equals TblKDMSConfig.SystemID
&& TblKDMSConfig.ConfigurationDate == (from TblKDMS_conf in this._db.GetTable<KDMS_configuration>()
where TblKDMS_conf.SystemID == TblKDMSConfig.SystemID
select TblKDMS_conf.ConfigurationDate).Max())
As i have try with && but it did not work....
it is done as on new{x.field1,x.field2} equals new{y.field1,y.field2}
var somedata = (from TblKDMS_conf in this._db.GetTable<KDMS_configuration>()
where TblKDMS_conf.SystemID == TblKDMSConfig.SystemID select TblKDMS_conf.ConfigurationDate).Max();
Queryable<getKDMS> query = (from VD in this._db.GetTable<KDMS_dynamic>()
join TblKDMS in this._db.GetTable<KDMS_definition>() on VD.SystemID equals TblKDMS.SystemID
join TblKDMSType in this._db.GetTable<KDMS_typeid>().DefaultIfEmpty() on TblKDMS.TypeID equals TblKDMSType.TypeID
join TblKDMSConfig in this._db.GetTable<KDMS_configuration>() on new {TblKDMS.SystemID,TblKDMSConfig.ConfigurationDate} equals new{TblKDMSConfig.SystemID,somedata}
Move the AND condition to your WHERE clause. Writing
SELECT * FROM Table1
INNER JOIN Table2 ON *first condition* AND *second condition*
WHERE *third condition*
is exactly the same as writing
SELECT * FROM Table1
INNER JOIN Table2 ON *first condition*
WHERE *second condition* AND *third condition*
I think you need to use the where operator in place of the &&.

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
});

Categories