繁体   English   中英

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

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

我有一个非常简单的 SQL 查询,但结果证明转换为 LINQ to SQL(或 LINQ to Entity)是一场噩梦。 我一直在阅读很多问题和文章,但我无法让它发挥作用。 这是 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

现在在所有研究和多次尝试之后,我想出了以下代码,它编译但抛出 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();

我也试过不分组,不是“加入”而是从哪里选择,而不是将计数放入组中,而是选择新的,而不是按添加/删除/现有进行分组。 没有任何效果,我尝试的越多,我就越不明白这是怎么回事。 它只工作了一次,但没有“现有”计数(子项计数,即在同一个表上连接 - 请参阅上面的 SQL 查询)。

在我看来,这是一个简单的 SQL 查询。 我应该把它放在一个视图中吗? 甚至可以使用 LINQ 来实现这一点(如果没有子查询的话)?

感谢您的帮助!

编辑 1

下面的代码有效,但如果父母有 N 个孩子,它将在结果中出现 N 次。 因为没有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();

编辑 2

实际上,下面是一种有效并返回良好值的方法。 甚至上面的 SQL 查询也是错误的。 对垃圾邮件感到抱歉。

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();

编辑 3

有效的超级丑陋的 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

如果这是实体框架(您说的是 LINQ to Entity),那么您可以制作项目的操作和项目表导航属性。 然后,您可以使用视图模型来计算您的计数。 就像是:

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

那么你的查询就是:

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

暂无
暂无

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

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