Home > OS >  LINQ with joins and group generates enormous query and is slow
LINQ with joins and group generates enormous query and is slow

Time:07-18

My Entity structure is as follows:

public class DisbursementItem
{
    public int DisbursementNumber;
    public int IDDisbursementItem;
    public int IDReceiptItem;
    public decimal Amount;
    public decimal MeasureUnit;
    public decimal PricePerMU;
    public decimal PriceTotal;
    public Disbursement Disbursement_IDDisbursement;
    public int IDDisbursementNumber;
}

public class Disbursement
{
    public int DisbursementNumber;
    DateTime date;
    DisbursementType DType;
    string Note;
    string Subscriber;
    Subscriber SubscriberModel;
    string ItemType;
    int ProcessNumber;
}

public class Subscriber 
{
    public string Name
    public string Address;
    public string City;
}

public class DisbursementDescription
{
    public int IDDisbursementItem;
    public string Description;
}

public class Receipt
{
    public int IDReceiptItem;
    public int ItemNumber;
}

public class StorageCard 
{
    public int ItemNumber;
    public string StorageCardGroup;
    public string StorageCardName;
}

And my EF6 LINQ query is:

DateTime from;
DateTime to;
var result = context.DisbursementItem
    .Where(x => x.Disbursement_IDDisbursement.Date <= to && x.Disbursement_IDDisbursement.Date >= from)
    .Join(context.DisbursementDescription, di => di.IDDisbursementItem, dd => dd.IDDisbursementItem, (di, dd) => new {di = di, desc = dd.Description})
    .Join(context.Receipt, x => x.di.IDReceiptItem, r => r.IDReceiptItem, (x, r) => new { di = x.di, desc = x.desc, r = r })
    .Join(context.StorageCard, x => x.r.ItemNumber, sc => sc.ItemNumber, (x, sc) => new { di = x.di, desc = x.desc, r = x.r, sc = sc})
    .GroupBy(g => new {g.di.DisbursementNumber, g.sc.ItemNumber, g.di.MeasureUnit})
    .Select(x => new
    {
        Date = x.FirstOrDefault().di.Disbursement_IDDisbursement.Date,
        DisbursementNumber = x.Key.DisbursementNumber,
        DType = x.FirstOrDefault().di.Disbursement_IDDisbursement.DType,
        Note = x.FirstOrDefault().di.Disbursement_IDDisbursement.Note,
        Subscriber = x.FirstOrDefault().di.Disbursement_IDDisbursement.Subscriber,
        SubscriberName = x.FirstOrDefault().di.Disbursement_IDDisbursement.SubscriberModel.Name,
        SubscriberAddress = x.FirstOrDefault().di.Disbursement_IDDisbursement.SubscriberModel.Address,
        SubscriberCity = x.FirstOrDefault().di.Disbursement_IDDisbursement.SubscriberModel.City,
        ItemNumber = x.FirstOrDefault().sc.ItemNumber,
        StorageCardGroup = x.FirstOrDefault().sc.StorageCardGroup,
        StorageCardName = x.FirstOrDefault().sc.StorageCardName,
        Amount = x.Sum(y => y.di.Amount),
        PricePerMU = x.FirstOrDefault().di.PricePerMU,
        PriceTotal = x.Sum(y => y.di.PriceTotal),
        MeasureUnit = x.Key.MeasureUnit
        Desc = x.FirstOrDefault().desc,
    })

SELECT
    di.Date,
    di.DisbursementNumber,
    d.DType,
    d.Note,
    d.Subscriber,
    subs.Name,
    subs.Address,
    subs.City,
    sc.ItemNumber,
    sc.StorageCardGroup,
    sc.StorageCardName,
    Sum(di.Amount) as Amount,
    di.PricePerMU,
    Sum(di.PriceTotal) as PriceTotal,
    di.MeasureUnit,
    dd.Description

FROM
    DisbursementItem as di

INNER JOIN Disbursement as d 
ON di.IDDisbursementNumber = d.DisbursementNumber

INNER JOIN Receipt as r 
ON di.IDReceiptItem = r.IDReceiptItem

INNER JOIN StorageCard as sc
ON r.ItemNumber = sc.ItemNumber

INNER JOIN DisbursementDescription dd 
ON di.IDDisbuzrsementItem = dd.IDDisbursementItem

WHERE 
    di.Date <= ... and di.Date >= ...

GROUP BY
di.DisbursementNumber, sc.ItemNumber, di.MeasureUnit

That is the query in SQL that I want to achieve in EF

This query can take over a minute for a few hundred rows. How can I optimize it? I suspect the multiple joins is a problem and maybe also the Sum of some fields.

Also the database schema cannot be modified.

The query it generate is enormous. It's like a SELECT in SELECT in SELECT for like 40 times.

CodePudding user response:

Easiest way is to add all fields which are needed for result to grouping key. Rewritten query to Query syntax for readability and maintainability:

DateTime from;
DateTime to;

var query = 
    from di in context.DisbursementItem
    where di.Disbursement_IDDisbursement.Date <= to && di.Disbursement_IDDisbursement.Date >= from
    join dd in context.DisbursementDescription on di.IDDisbursementItem equals dd.IDDisbursementItem
    join r in context.Receipt on di.IDReceiptItem equals r.IDReceiptItem
    join sc in context.StorageCard on r.ItemNumber equals sc.ItemNumber
    group di by new 
    {
        di.DisbursementNumber, 
        sc.ItemNumber, 
        di.MeasureUnit, 
        di.Disbursement_IDDisbursement.Date, 
        di.Disbursement_IDDisbursement.DType, 
        di.Disbursement_IDDisbursement.Note,
        Subscriber = di.Disbursement_IDDisbursement.Subscriber,
        SubscriberName = di.Disbursement_IDDisbursement.SubscriberModel.Name,
        SubscriberAddress = di.Disbursement_IDDisbursement.SubscriberModel.Address,
        SubscriberCity = di.Disbursement_IDDisbursement.SubscriberModel.City,
        sc.ItemNumber,
        sc.StorageCardGroup,
        sc.StorageCardName,
        di.PricePerMU,
        Desc = dd.Description
    } into g
    select new 
    {
        g.Key.Date,
        g.Key.DisbursementNumber,
        g.Key.DType,
        g.Key.Note,
        g.Key.Subscriber,
        g.Key.SubscriberName,
        g.Key.SubscriberAddress,
        g.Key.SubscriberCity,
        g.Key.ItemNumber,
        g.Key.StorageCardGroup,
        g.Key.StorageCardName,
        g.Key.PricePerMU,
        g.Key.MeasureUnit,
        g.Key.Desc,

        Amount = g.Sum(x => x.Amount),
        PriceTotal = g.Sum(x => x.PriceTotal)        
    }

CodePudding user response:

you could try some kind of multithreading

the query could be splitted in parts and each part assigned to a task. in here you should find something useful (parallel section):

https://docs.microsoft.com/en-us/dotnet/api/system.threading.tasks?view=net-6.0

  • Related