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