简体   繁体   English

LINQ to SQL - 多个左联接、分组依据、同一个表上的左联接、计数

[英]LINQ to SQL - multiple left join, group by, left join on same table, count

I have a SQL query that is pretty simple, but turns out to be a nightmare to transform into LINQ to SQL (or LINQ to Entity).我有一个非常简单的 SQL 查询,但结果证明转换为 LINQ to SQL(或 LINQ to Entity)是一场噩梦。 I've been reading lots of questions and articles and I just cannot manage to make it work.我一直在阅读很多问题和文章,但我无法让它发挥作用。 Here is the SQL query (Items table 1-N to Operations table, Items has a ParentItemId column):这是 SQL 查询(Items 表 1-N 到 Operations 表,Items 有一个 ParentItemId 列):

select 
      i.Id
    , i.Code
    , count(oAdd.ItemId) "Added"
    , count(oRem.ItemId) "Removed"
    , count(iChild.Id) "Existing"

from items i

left join operations oAdd on oAdd.ItemId = i.Id and oAdd.OperationTypeId = 10
left join operations oRem on oRem.ItemId = i.Id and oRem.OperationTypeId = 20
left join items iChild on iChild.ParentItemId = i.Id

group by 
      i.Id
    , i.Code

Now after all the reseach and multiple attemps, I came up with the following code, which compiles but throws a EntityCommandCompilation exception ("The nested query is not supported. Operation1='GroupBy' Operation2='MultiStreamNest'"):现在在所有研究和多次尝试之后,我想出了以下代码,它编译但抛出 EntityCommandCompilation 异常(“不支持嵌套查询。Operation1='GroupBy' Operation2='MultiStreamNest'”):

var query =

    from item in dbContext.Items

    join oAdd in dbContext.Operations on item.Id equals oAdd.ItemId into oAddJoin
    join oRem in dbContext.Operations on item.Id equals oRem.ItemId into oRemJoin
    join oChild in dbContext.Items on item.Id equals oChild.ParentItemId into oChildJoin

    from oAddLeftJoin in oAddJoin.Where(o => o.OperationTypeId == (int)OperationTypes.AddTo).DefaultIfEmpty()
    from oRemLeftJoin in oRemJoin.Where(o => o.OperationTypeId == (int)OperationTypes.RemoveFrom).DefaultIfEmpty()
    from oChildLeftJoin in oChildJoin.DefaultIfEmpty()

    group oChildLeftJoin by new
    {
        ItemId = item.Id,
        ItemCode = item.Code,
        Added = oAddJoin.Count(),
        Removed = oRemJoin.Count(),
        Existing = oChildJoin.Count()
    }
    into oChildLeftJoinGrouped

    select new
    {
        oChildLeftJoinGrouped.Key.ItemId,
        oChildLeftJoinGrouped.Key.Added,
        oChildLeftJoinGrouped.Key.Removed,
        oChildLeftJoinGrouped.Key.Existing
    };

var summaryList = query.ToList();

I've also tried not grouping, not "joining into" but selecting from where instead, not putting the count in the group by but in the select new, not grouping by added/removed/existing.我也试过不分组,不是“加入”而是从哪里选择,而不是将计数放入组中,而是选择新的,而不是按添加/删除/现有进行分组。 Nothing works, and the more I try the less I feel I understand what this is about.没有任何效果,我尝试的越多,我就越不明白这是怎么回事。 It worked only once but without the "Existing" count (count of child items, ie join on the same table - see SQL query above).它只工作了一次,但没有“现有”计数(子项计数,即在同一个表上连接 - 请参阅上面的 SQL 查询)。

Seems to me like this is an easy SQL query.在我看来,这是一个简单的 SQL 查询。 Should I put that in a view instead?我应该把它放在一个视图中吗? Is it even possible to achieve this with LINQ (if possible without subqueries)?甚至可以使用 LINQ 来实现这一点(如果没有子查询的话)?

Thank you for your help!感谢您的帮助!

EDIT 1编辑 1

The code below works but if a parent has N children it will appear N times in the result.下面的代码有效,但如果父母有 N 个孩子,它将在结果中出现 N 次。 Because there is no group by.因为没有group by。

var query =

    from item in dbContext.Items

    join oAdd in dbContext.Operations on item.Id equals oAdd.ItemId into oAddJoin
    join oRem in dbContext.Operations on item.Id equals oRem.ItemId into oRemJoin

    from oAddLeftJoin in oAddJoin.Where(o => o.OperationTypeId == (int)OperationTypes.AddTo).DefaultIfEmpty()
    from oRemLeftJoin in oRemJoin.Where(o => o.OperationTypeId == (int)OperationTypes.RemoveFrom).DefaultIfEmpty()

    let existingCount = dbContext.Items.Count(i => i.ParentItemId == item.Id)

    select new
    {
        item,
        Added = oAddJoin.Count(),
        Removed = oAddJoin.Count(),
        existingCount
    };

var summaryList = query.ToList();

EDIT 2编辑 2

Actually, below is the one which works and return good values.实际上,下面是一种有效并返回良好值的方法。 Even the SQL Query above is wrong.甚至上面的 SQL 查询也是错误的。 Sorry about the spam.对垃圾邮件感到抱歉。

var query = 

    from item in dbContext.Items

    let addedCount    = dbContext.Operations.Count(o => o.ItemId == item.Id && o.OperationTypeId == (int)OperationTypes.AddTo)
    let removedCount  = dbContext.Operations.Count(o => o.ItemId == item.Id && o.OperationTypeId == (int)OperationTypes.RemoveFrom)
    let existingCount = dbContext.Items.Count(i => i.ParentItemId == item.Id)

    select new
    {
        item,
        Added = addedCount,
        Removed = removedCount,
        Existing = existingCount
    };

var summaryList = query.Distinct().ToList();

EDIT 3编辑 3

The super ugly SQL query that works:有效的超级丑陋的 SQL 查询:

select distinct
      i.Id
    , i.Code
    , (select count(*) from operations oAdded where oAdded.itemid = i.id and oAdded.operationtypeid = 10) "Added"
    , (select count(*) from operations oAdded where oAdded.itemid = i.id and oAdded.operationtypeid = 20) "Removed"
    , (select count(*) from items ic where ic.ParentItemId = i.id) "Existing"
from items i

If this is Entity Framework (you said LINQ to Entity), then you could make the operations and item tables navigation properties of item.如果这是实体框架(您说的是 LINQ to Entity),那么您可以制作项目的操作和项目表导航属性。 Then, you could use a view model to calculate your counts.然后,您可以使用视图模型来计算您的计数。 Something like:就像是:

public class ViewModel : Item
{
    public int Added {get;set;}
    public int Removed {get;set;}
    public int Existing {get;set;}

    public ViewModel(Item i) {
        this.id = i.id;
        this.code = i.code;
        this.Added = i.operations.Where(o => o.operationTypeID == 10).Count;
        this.Removed = i.operations.Where(o => o.operationTypeID == 20).Count;
        this.Existing = i.Items.Count;
   }
}

Then your query would just be:那么你的查询就是:

dbContext.Items.Select(i => new ViewModel(i)).ToList();

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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