简体   繁体   中英

LINQ with joins and group generates enormous query and is slow

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.

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

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM