Define nested derived table in Entity Framework - c#

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

Related

left join linq query in FF4

I have the following SQL Query:
SELECT DISTINCT *
FROM Documents d
LEFT JOIN
Invoices
ON (i.invoicedocumentid = d.id or i.jobsheetdocumentid = d.id)
INNER JOIN
PurchaseOrders PO ON i.poid = PO.id
LEFT JOIN
HelpDeskFaults f
ON f.id = PO.helpDeskFaultId
LEFT JOIN stores s
ON s.id = f.storeid
WHERE s.name = 'Linden Drive'
OR d.id in (
SELECT u.id as 'docid'
FROM documents u
INNER JOIN stores s
ON u.storeid = s.id
WHERE s.name = 'Linden Drive'
)
ORDER BY d.Id
So far my linq query looks like this:
var documents = from doc in context.Documents
from invoice in context.Invoices
join po in context.PurchaseOrders on invoice.PurchaseOrder.PurchaseOrderId equals po.PurchaseOrderId
join hdf in context.HelpDeskFaults on po.HelpdeskFaultId equals hdf.ID into hdfpo
from hs in hdfpo.DefaultIfEmpty()
join store in context.Stores on hs.StoreID equals store.ID into hsstore
from hss in hsstore.DefaultIfEmpty()
where hss.Name.Contains(jobSearchParams.StoreName) && (invoice.InvoiceDocumentId == doc.ID || invoice.JobSheetInvoiceId == doc.ID)
select doc;
But the sql that is produced is nothing like what I expected.
Can anyone give me any pointers of how to improve my linq query.
This is what I would translate your SQL query into
var results =
(from doc in context.Documents
from invoice in (
from inv in context.Invoices
where inv.InvoiceDocumentId == doc.ID || inv.JobSheetInvoiceId == doc.ID
select inv).DefaultIfEmpty()
from hs in invoice.PurchaseOrder.HelpDeskFaults.DefaultIfEmpty()
from hss in hs.Stores.DefaultIfEmpty()
where hss.Name == "Linden Drive" || doc.Store.Name == "Linden Drive"
order by doc.ID
select new
{
Document = doc,
Invoice = invoice,
invoice.PurchaseOrder,
HelpDeskFault = hs,
Store = hss,
}).Distinct();
I included that anonymous class of the 5 entities because your SQL is using Select *. I would recommend changing it to only return the minimum required set of fields.

Converting left outer join on two queries to LINQ

I have below sql query which I want to convert into LINQ to obtain exactly same results and returned by below query
select *
from (
select distinct DocID
from UserViewDoc
where UserViewDoc.UVID in (102558)) a
left outer join
(
select distinct UserViewDoc.DocID
from UserViewDoc
inner join UserViewHeader on UserViewDoc.UVID = UserViewHeader.UVID
where UserViewDoc.UVID not in (102558)
and UserViewHeader.IsLock = 1) b on a.DocID = b.DocID
where b.DocID is null
)
What I have tried so far is below LINQ statement
var v = (from uvd in this.ViewSelectorControl.LDReviewContext.GetTable<UserViewDoc>()
where IDs.Contains(uvd.UVID)
select new { uvd.DocID, uvd.UVID });
var c = ((from uvd in this.ViewSelectorControl.LDReviewContext.GetTable<UserViewDoc>()
join uvh in this.ViewSelectorControl.LDReviewContext.GetTable<UserViewHeader>() on uvd.UVID equals uvh.UVID
where !IDs.Contains(uvh.UVID) && uvh.IsLock == true
select new { uvd.DocID, uvd.UVID } ));
var d = (from id in v
join ids in c on id.UVID equals ids.UVID into vc
from sub in vc.DefaultIfEmpty()
where sub == null
select id);
The problem I am facing is running the SQL query is returning 30583 records and LINQ version of it is returning all of the 30613 records
I rewrote the query to be exactly like the sql query, i believe that this is the way:
var firstQuery = (from u in this.ViewSelectorControl.LDReviewContext.GetTable<UserViewDoc>()
where IDs.Contains(u.UVID)
select u.DocID).Distinct();
var innerQuery = (from u in this.ViewSelectorControl.LDReviewContext.GetTable<UserViewDoc>()
join uvh in this.ViewSelectorControl.LDReviewContext.GetTable<UserViewHeader>() on uvh.UVID equals u.UVID
where IDs.Contains(u.UIVD) == false
&& uvh.IsLock
select u.DocID).Distinct();
var resultQuery = from f in firstQuery
join i in innerQuery on i equals f into lout
from i in lout.DefaultIfEmpty()
where i == null
select f;

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 Implement this Sql block contain (Having , Join and Group By) with Linq

my sql statement is
SELECT c.type,c.title,c.datereg, d.ranknum
FROM T_News AS c
INNER JOIN (
SELECT a.id, COUNT(*) AS ranknum
FROM T_News AS a
INNER JOIN T_News AS b
ON (a.type = b.type)
AND (a.datereg >= b.datereg)
GROUP BY a.id
HAVING COUNT(*) <= 3
) AS d ON (c.id = d.id)
ORDER BY c.type, d.ranknum
that i get http://rickosborne.org/blog/2008/01/sql-getting-top-n-rows-for-a-grouped-query/
for Getting TOP N rows for a grouped query
EFUnitOfWork EF = new EFUnitOfWork();
T_NewsRepository News = new T_NewsRepository();
News.UnitOfWork = EF;
var query =
from news1 in News.All()
join news2 in News.All()
on news1.type equals news2.type into resjoin
group news1 by news1.id into idgroup
where idgroup.Count() <= 3
select new { idgroup };
var x = query.ToList();
I did not get any error , but "where idgroup.Count() <= 3" did not work and i get all rows in db as result
Break it down into it's smallest components and then compose the larger query from that. Let's start with the innermost query that makes sense:
SELECT
a.id, COUNT(*) AS ranknum
FROM
T_News AS a
INNER JOIN T_News AS b ON
(a.type = b.type) AND
(a.datereg >= b.datereg)
GROUP BY
a.id
HAVING
COUNT(*) <= 3
I'd convert this to:
// Items with counts/ranknum
var ranknum =
from a in News.All()
join b in News.All() on
a.type equals b.type
where
a.datereg > b.datereg
group by a.id into g
select new { g.Key as id, g.Count() as ranknum };
// Filter the ranknum.
ranknum = ranknum.Where(rn => rn.ranknum <= 3);
Then joining that with the outer query:
SELECT
c.type,c.title,c.datereg, d.ranknum
FROM
T_News AS c
INNER JOIN (<sub-query from above>) as d ON
c.id = d.id
ORDER BY
c.type, d.ranknum
That part becomes simple, as it's just a join between two existing queries.
var query =
from c in News.All()
join rn in ranknum on c.id = rn.id
orderby c.type, rn.ranknum
select new { c.type, c.title, c.datereg, rn.ranknum };
Chances are the SQL that LINQ-to-Entities generates for this is going to look really ugly, and probably be inefficient, in which case, you might want to consider placing this logic in a stored procedure and then calling that through LINQ-to-Entities (which is generally true for more complex queries).

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