Linq query: inner join with a count() group by subquery - c#

We have some Machines having Devices attached. Not all the machines have devices atached and the devices cand be moved between machines. The devices generate Errors and we need to count those errors occured in the past day.
We have four tables: Machines (with Id and Code), Devices (with Id and Code), a pairing table DevicesMachines (Id, IdMachine, IdDevice, From datetime, To datetime) and Errors(Id, IdDevice, Moment datetime, Description).
The working SQL query is this:
Select m.Id, m.Code,
Coalesce(d.Code, 'NA') As DeviceCode,
Coalesce(Err.ErrorCnt,0) As ErrorCnt
From Machines As m
Left Outer Join (Select IdMachine, IdDevice From DevicesMachines as dm
Where GetDate() Between dm.From And dm.To) As dm on m.Id=dm.IdMachine
Left Outer Join Devices As d on dm.IdDevice=d.Id
Left outer join
( Select IdMachine, Count(Id) As ErrorCnt From Errors as er
Where er.Moment >= DateAdd(day,-1,GetUtcDate())
Group By IdMachine) As Err
On m.Id=Err.IdMachine
I have tried many syntaxes, one of which is below:
using ( DataContextM dcMachines = new dataContextM())
{
IEnumerable<MachineRow> lstM =
from m in dcMachines.Machines
from dm in dcMachines.DevicesMachines.Where(dm => (dm.IdMachine == m.Id) && (dm.From <= DateTime.Now) && (dm.To >= DateTime.Now)).DefaultIfEmpty()
from d in dcMachines.Devices.Where(d => d.Id == dm.IdDevice).DefaultIfEmpty()
from er in dcMachines.Errors
.Where(er => (er.Moment >= DateTime.Now) && (er.Moment <= DateTime.Now.AddDays(-1)))
.GroupBy(er => er.IdMachine)
.Select(er => new { IdMachine = er.Key, ErrorCnt = er.Count() })
.Where(er=> er.IdMachine==m.Id).DefaultIfEmpty()
select new MachineRow
{
Id = amId,
Code = m.Code,
DeviceCode = (d == null) ? "NA" : d.DeviceCode,
IdDevice = (d == null) ? 0: d.Id,
ErrorCnt = (er == null) ? 0 : er.ErrorCnt
};
}
I failed to find the right Linq syntax and I need your help.
Thank you,
Daniel

Based on the SQL you provided, I created what I think is the equivalent EF LINQ expression:
using (var dcMachines = new DataContextM())
{
var now = DateTime.Now;
var utcYesterday = DateTime.UtcNow.AddDays(-1);
var devicesMachinesQuery =
from dm in dcMachines.DevicesMachines
where dm.From <= now && dm.To >= now
join d in dcMachines.Devices on dm.IdDevice equals d.Id into dItems
from d in dItems.DefaultIfEmpty()
select new
{
dm.IdMachine,
dm.IdDevice,
DeviceCode = d != null ? d.Code : "NA"
};
var errorsQuery =
from err in dcMachines.Errors
where err.Moment >= utcYesterday
select err;
IEnumerable<MachineRow> lstM =
from m in dcMachines.Machines
join dm in devicesMachinesQuery on m.Id equals dm.IdMachine into dmItems
from dm in dmItems.DefaultIfEmpty()
select new MachineRow
{
Id = m.Id,
Code = m.Code,
DeviceCode = dm != null ? dm.DeviceCode : "NA",
IdDevice = dm != null ? dm.IdDevice : 0,
ErrorCnt = (
from err in errorsQuery
where err.IdMachine == m.Id
select err.Id
)
.Count()
};
}
I made some tests in memory and it seems to yield the same behavior as your provided SQL query.

Related

Linq Query Throws Timeout in code but Works fine on LinqPad

When I run the Linq query on LinqPad it takes only 4-5 seconds to return 5000 rows but when I run that same query in my code using Entity Framework it throws timeout.
What could be the possible issues?
Thanks in advance.
Below is the query:
var resultSale =
from product in Products
join productInfo in ProductInfoSummaries on product.ID equals productInfo.ProductID
join productDetail in ProductDetails on new { Id = product.ID, storeId = product.CreatedInStore } equals new { Id = productDetail.ProductID, storeId = productDetail.StoreID }
join productInventoryOtherStore in InventoryOtherStores on product.ID equals productInventoryOtherStore.ProductID
into productInventories
from productInventoryOtherStore in productInventories.DefaultIfEmpty()
join saleLine in SaleLines on productDetail.ID equals saleLine.ArtikkelNr
join sales in Sales on saleLine.OrderID equals sales.ID
where saleLine.ArtikkelNr != null
&& saleLine.DatoTid >= new DateTime(2018, 01, 01)
&& saleLine.DatoTid <= new DateTime(2019,11,21)
&& sales.StoreID == 14
&& (sales.OrderType == 1 || sales.OrderType == 2 || sales.OrderType == 4 || sales.OrderType == 6)
&& productDetail.SupplierProductNo != null
&& productDetail.Deleted == null
&& (productInfo.Inactive == null || productInfo.Inactive == false)
&& (product.CreatedInStore == 14 || product.CreatedInStore == 0 || product.CreatedInStore == null)
group new { saleLine.AntallEnheter, sales.OrderType } by new { product.ID, productInventoryOtherStore.Amount } into g
select new ProductSaleSummaryVM
{
ID = g.Key.ID,
Inventory = (double)g.Key.Amount,
TotalSold = g.Sum(x => x.OrderType !=4 ? x.AntallEnheter : 0) ?? 0,
TotalWastage = g.Sum(x => x.OrderType ==4 ? x.AntallEnheter : 0) ?? 0,
TotalOrderedQty = 0
};
var resultSupplierOrder =
from supplierOrderLine in SupplierOrderLines
join supplierOrder in SupplierOrders on supplierOrderLine.SupplierOrderID equals supplierOrder.ID
where supplierOrderLine.Deleted == null
&& supplierOrder.Status != 1
&& supplierOrder.StoreID == 14
group supplierOrderLine by supplierOrderLine.ProductID into g
select new ProductOrderDetailsVM
{
ID = g.Key,
TotalOrderedQty = (double)g.Sum(x => x.ConsumerQuantity - x.QuantiyReceived)
};
var r =
(from resSale in resultSale
join resSupplierOrder in resultSupplierOrder on resSale.ID equals resSupplierOrder.ID
into resSupplierOrders
from resSupplierOrder in resSupplierOrders.DefaultIfEmpty()
orderby resSale.ID
select new ProductSaleSummaryVM
{
ID = resSale.ID,
Inventory = resSale.Inventory,
TotalSold = resSale.TotalSold,
TotalWastage = resSale.TotalWastage,
TotalOrderedQty = resSupplierOrder.TotalOrderedQty ?? 0
})
.Where(x => x.Inventory + x.TotalOrderedQty < x.TotalSold);
r.Dump();
Try using entity framework within linqpad see if it gives you any clues. see this link on how to use entity framework in linqpad
https://www.linqpad.net/EntityFramework.aspx
This behaviour could be related to parameter sniffing - check this article for details

Conditional retrieval of data through linq in C#

var Getdetails = (from p in XYZDb.tblPulls
join
ro in XYZDb.tblRentalOrders
on p.AffCode equals ro.AffCode.Value
join
tpsb in XYZDb.tblPullSheetBatchProcessings
on p.PullNo.ToString() equals tpsb.PullSheet
select new
{
PullNos = p.PullNo,
AffCode = p.AffCode,
TotalItems = p.TotalItems,
p.PostedOn,
p.UpdatedOn,
p.IsPrinted,
BatchName = tpsb.BatchName
})
.Where(i => i.PostedOn >= from_date && i.PostedOn <= to && i.IsPrinted != null).Distinct();
In the above code only the pullno having BatchName are coming, i want to retrieve all the pullno within that timezone, and if it is batched, then the BatchName will also appear. I am stuck on that. Any kind of help will be appreciated. Feel free to ask any question.
Use left outer join,
var Getdetails = (from p in XYZDb.tblPulls
join
ro in XYZDb.tblRentalOrders
on p.AffCode equals ro.AffCode.Value
join
tpsb in XYZDb.tblPullSheetBatchProcessings
on p.PullNo.ToString() equals tpsb.PullSheet into pt
from batch in pt.DefaultIfEmpty()
select new
{
PullNos = p.PullNo,
AffCode = p.AffCode,
TotalItems = p.TotalItems,
p.PostedOn,
p.UpdatedOn,
p.IsPrinted,
BatchName = (batch == null ? String.Empty : batch.BatchName )
})
.Where(i => i.PostedOn >= from_date && i.PostedOn <= to && i.IsPrinted != null).Distinct();

Converting an SQL Containing inner and Outer Joins into Linq

I need to convert an SQL query to Linq/Lambda expression, I am trying doing the same but not getting the desired results.
SQL:
SELECT b.*, n.notes
FROM Goal_Allocation_Branch as b
INNER JOIN Goal_Allocation_Product as g
on b.Product = g.ProductID
and b.Year = g.Year
left join Goal_Allocation_Branch_Notes as n
on b.branchnumber = n.branch
and n.year = ddlYear
WHERE b.Year = ddlYear
and g.Show = 1
and branchnumber = ddlBranch
I am new to Linq , I am getting error on Join Clause , and X is not containing any data from first Join
var result = (from br in _DB_Branches.Goal_Allocation_Branches
join pr in _DB_Product.Goal_Allocation_Products on new { br.Product, br.Year } equals new {Product= pr.ProductID, Year= pr.Year }
join n in _DB_Notes.Goal_Allocation_Branch_Notes.Where(n => n.Year == ddlYear) on br.BranchNumber equals n.Branch into Notes
from x in Notes.DefaultIfEmpty()
select new BranchNotesViewModel
{
Year = x.Year,
BranchNumber = x.Branch,
ProductID = x.ProdID
}
).ToList();
Update: My First Join clause initially giving error "The type of one of the expression in Join Clause is incorrect " is resolved, when I Changed On Clause
from
"on new { br.Product, br.Year } equals new {pr.ProductID, pr.Year}"
"on new { br.Product, br.Year } equals new {Product=pr.ProductID,Year= pr.Year}"
still not getting desired results as expected from above SQL query. Please advise..
It should be something like this (see note):
var result =
(from br in _DB_Branches.Goal_Allocation_Branches
join pr in _DB_Product.Goal_Allocation_Products
on br.Product equals pr.ProductID
from n in _DB_Notes.Goal_Allocation_Branch_Notes.Where(x=>
x.branch == br.branchnumber
&& x.year == ddlYear
).DefaultIfEmpty()
where
br.Year == ddlYear
&& and br.Year == pr.Year
&& pr.Show == 1
&& br.branchnumber == ddlBranch
select new BranchNotesViewModel
{
Year = ...,
BranchNumber = ...,
ProductID = ...
}
).ToList();
Note: Change the select, to the properties you want.
Edit: fixed some syntax errors.
I finally figured out the correct answer. Working absolutely fine
var result = (from br in _DB_Branches.Goal_Allocation_Branches
join pr in _DB_Product.Goal_Allocation_Products on new { br.Product, br.Year } equals new { Product = pr.ProductID, Year = pr.Year }
join n in _DB_Notes.Goal_Allocation_Branch_Notes.Where(n=>n.Year==ddlYear) on br.BranchNumber equals n.Branch into Notes
where br.Year==ddlYear
&& pr.Show== true
&& br.BranchNumber==ddlBranch
from x in Notes.DefaultIfEmpty()
select new BranchNotesViewModel
{
Year=x.Year,
BranchNumber=x.Branch,
ProductID=br.Product,
Notes = x.Notes,
//All other fields needed
}
).ToList();

LINQ query with sub where clause

I have a SQL query :
SELECT [Paypoint]
,[Department]
,[EmployeeCode]
,[Gender]
,[EmployeeTitle]
,[Initials]
,[Surname]
,[ItemsIssuedDate]
,[ItemsIssuedStockNumber]
FROM [MyTable] AS a
WHERE
(
[ItemsIssuedDate] = ( SELECT max([ItemsIssuedDate])
FROM [MyTable] AS b
WHERE a.[Paypoint] = b.[Paypoint]
AND a.[Department] = b.[Department]
AND a.[EmployeeCode] = b.[EmployeeCode]
AND a.[Gender] = b.[Gender]
AND a.[Surname] = b.[Surname]
)
How would one get the comparitive LINQ query ? I cannot use the SQL query as the Data is already in a DataSet, and now needs to be modified further...
I have attempted, but this does not work :
var query = from a in excelTable
where
(
from c in excelTable
group c by new
{
c.Paypoint,
c.EmployeeCode
} into g
where string.Compare(a.Paypoint, g.Key.Paypoint) == 0 && string.Compare(a.EmployeeCode, g.Key.Paypoint) == 0
select g.Key.Paypoint
)
select a;
var query = from a in MyTable
group a by new {
a.Paypoint,
a.Department,
a.EmployeeCode,
a.Gender,
a.Surname
} into g
select g.OrderByDescending(x => x.ItemsIssuedDate)
//.Select(x => new { required properties })
.First();
You can also select anonymous object with required fields only. Up to you.
Your most direct with the SQL query will be:
var query =
from a in excelTable
let maxIssueDate =
(from b in excelTable
where a.Paypoint == b.Paypoint &&
a.Department == b.Department &&
a.EmployeeCode == b.EmployeeCode &&
a.Gender == b.Gender &&
a.Surname == b.Surname
select b.ItemsIssueDate).Max()
where a.ItemsIssueDate == maxIssueDate
select a;

why does LINQ To SQL result in SQL like this?

When LINQ translates the below syntax to SQL, the (inner) where clause gets moved to the outer-most query. That's super-unfriendly to the database. I wrote this like Hibernate's HQL (is this appropriate?), and I've written SQL for many moons.
Can anyone help explain what gives, or point me in the way of a resolution?
var rc = (
from dv in (
from dv_j in (
from x in adc.JobManagement
join j in adc.Job on x.JobId equals j.JobId
join js in adc.JobStatus on j.StatusId equals js.JobStatusId
join cm in adc.ClientManagement on j.ClientId equals cm.ClientId
join o in adc.User on cm.UserId equals o.UserId
join jm in adc.JobManagement on j.JobId equals jm.JobId
where
(x.UserId == aid || cm.UserId == aid)
&& (j.StatusDate == null || j.StatusDate >= getFromDate())
&& (jm.ManagementRoleCode == MR_MANAGER)
select new
{
j.JobId,
Job = j.InternalName == null ? j.ExternalName : j.InternalName,
JobStatusDate = j.StatusDate,
JobStatus = js.Code,
Owner = o.Username,
Role = jm.ManagementRoleCode
})
join s in adc.Submission on dv_j.JobId equals s.JobId into dv_s
from s in dv_s.DefaultIfEmpty()
select new
{
dv_j.JobId,
dv_j.Job,
dv_j.JobStatusDate,
dv_j.JobStatus,
dv_j.Owner,
dv_j.Role,
s.SubmissionId,
s.CandidateId,
s.SubmissionDate,
StatusDate = s.StatusDate,
StatusId = s.StatusId
})
join c in adc.Candidate on dv.CandidateId equals c.CandidateId into dv_c
join ss in adc.SubmissionStatus on dv.StatusId equals ss.SubmissionStatusId into dv_ss
from c in dv_c.DefaultIfEmpty()
from ss in dv_ss.DefaultIfEmpty()
orderby
dv.StatusId == null ? dv.StatusDate : dv.JobStatusDate descending,
dv.Job,
c.LastName,
c.NickName,
c.FirstName
select new Projects
{
Id = dv.JobId,
Project = dv.Job,
Submitted = dv.SubmissionDate,
Candidate = FormatIndividual(c.LastName, c.FirstName, c.NickName),
Status = dv.StatusId == null ? ss.Code : dv.JobStatus,
StatusDate = dv.StatusId == null ? dv.StatusDate : dv.JobStatusDate,
Role = dv.Role,
Owner = dv.Owner
});
Try breaking down the one statement into two. This would work as it cannot move the where to a place that doesn't exist yet. This does make multiple round trips to the database, but it is better than having most of several large tables being joined then culled. I would try this:
var inMemoryTable = (
from x in adc.JobManagement
join j in adc.Job on x.JobId equals j.JobId
join js in adc.JobStatus on j.StatusId equals js.JobStatusId
join cm in adc.ClientManagement on j.ClientId equals cm.ClientId
join o in adc.User on cm.UserId equals o.UserId
join jm in adc.JobManagement on j.JobId equals jm.JobId
where
(x.UserId == aid || cm.UserId == aid)
&& (j.StatusDate == null || j.StatusDate >= getFromDate())
&& (jm.ManagementRoleCode == MR_MANAGER)
select new
{
j.JobId,
Job = j.InternalName == null ? j.ExternalName : j.InternalName,
JobStatusDate = j.StatusDate,
JobStatus = js.Code,
Owner = o.Username,
Role = jm.ManagementRoleCode
});
var rc = (
from dv in (
from dv_j in inMemoryTable
join s in adc.Submission on dv_j.JobId equals s.JobId into dv_s
from s in dv_s.DefaultIfEmpty()
select new
{
dv_j.JobId,
dv_j.Job,
dv_j.JobStatusDate,
dv_j.JobStatus,
dv_j.Owner,
dv_j.Role,
s.SubmissionId,
s.CandidateId,
s.SubmissionDate,
StatusDate = s.StatusDate,
StatusId = s.StatusId
})
join c in adc.Candidate on dv.CandidateId equals c.CandidateId into dv_c
join ss in adc.SubmissionStatus on dv.StatusId equals ss.SubmissionStatusId into dv_ss
from c in dv_c.DefaultIfEmpty()
from ss in dv_ss.DefaultIfEmpty()
orderby
dv.StatusId == null ? dv.StatusDate : dv.JobStatusDate descending,
dv.Job,
c.LastName,
c.NickName,
c.FirstName
select new Projects
{
Id = dv.JobId,
Project = dv.Job,
Submitted = dv.SubmissionDate,
Candidate = FormatIndividual(c.LastName, c.FirstName, c.NickName),
Status = dv.StatusId == null ? ss.Code : dv.JobStatus,
StatusDate = dv.StatusId == null ? dv.StatusDate : dv.JobStatusDate,
Role = dv.Role,
Owner = dv.Owner
});

Categories

Resources