LINQ to SQL - 多个左连接,分组依据,左连接在同一个表上,计数

时间:2015-11-04 15:15:39

标签: c# sql-server linq-to-sql group-by left-join

我有一个非常简单的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'&#34):

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

我也尝试过不分组,而不是"加入"但是从哪里选择,而不是将计数放在组中但是在选择新的,而不是通过添加/删除/现有分组。什么都行不通,我越少尝试越少,我觉得我明白这是什么。它只工作一次,但没有"现有" count(子项的数量,即在同一个表上连接 - 请参阅上面的SQL查询)。

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

感谢您的帮助!

编辑1

以下代码有效,但如果父母有N个孩子,则会在结果中出现N次。因为没有分组。

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

1 个答案:

答案 0 :(得分:1)

如果这是Entity Framework(您说LINQ to Entity),那么您可以创建item的操作和项表导航属性。然后,您可以使用视图模型来计算您的计数。类似的东西:

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