繁体   English   中英

Linq Left join,where,group by,count()

[英]Linq Left join, where, group by, count()

我需要帮助在linq语句中执行左连接。 我的T-sql查询按预期工作,但我似乎无法从Linq获得想要的结果。 我也意识到像我这样的问题很多,但我似乎无法对我的案例应用任何解决方案。

产品表

+---+------------+-----------+
|   |transportID |  Type(int)|
+---+------------+-----------+
| 1 | 5          | 1         |
| 2 | 5          | 3         |
| 3 | 6          | 3         |
+---+------------+-----------+

商店

+---+------------+-------------+
|   |Name        |Type1(string)|
+---+------------+-------------+
| 1 | Ho         | 1           |
| 2 | He         | 2           |
| 3 | Be         | 3           |
| 4 | Ke         | 4           |
| 5 | Fe         | 5           |
+---+------------+-------------+

我想要的结果是

+---+------------+-------------+
|   |Type        |Count        |
+---+------------+-------------+
| 1 | 1          | 1           |
| 2 | 2          | 0           |
| 3 | 3          | 1           |
| 4 | 4          | 0           |
| 5 | 5          | 0           |
+---+------------+-------------+

我的tsql按预期工作

SELECT 
    Type1,
    Count(Pro.transportId) as Count

FROM dbo.stores as sto

left Join dbo.products as pro on (sto.Type1 = pro.Type AND pro.transportId=5)

Where Type1 is not null
  group by Type1
  ORDER BY Type1 * 1 ASC

我的Linq尝试返回此。

+---+------------+-------------+
|   |Type        |Count        |
+---+------------+-------------+
| 1 | 1          | 1           |
| 3 | 3          | 1           |
+---+------------+-------------+

Linq声明。

var res =   (from sto in _context.Stores
                             join pro in _context.Products on sto.Type1 equals System.Data.Objects.SqlClient.SqlFunctions.StringConvert((double)pro.Type).Trim()
                             where pro.transportId == transportId
                             group pro by pro.Type1 into pt1
                             select new TypeTransportation()
                             {
                                 Type = pt1.Key, // Needs to be int
                                 Count = pt1.Count()
                             }).ToList();

我试过做一些defaultifempty但似乎无法使它工作。

这是MSDN链接“如何:执行左外连接”与LINQ: https//msdn.microsoft.com/en-gb/library/bb397895.aspx

你的代码应该是这样的:

        var res = (from sto in _context.Stores
               join pro in _context.Products on sto.Type1 equals System.Data.Objects.SqlClient.SqlFunctions.StringConvert((double)pro.Type).Trim() into grpJoin
               from product in grpJoin.DefaultIfEmpty()
               where product.transportId == transportId
               group product by product.Type1 into pt1
               select new TypeTransportation()
               {
                   Type = pt1.Key, // Needs to be int
                   Count = pt1.Count()
               }).ToList();

..最后我做到了..

      var transportId = 5;
      var res = from s in _context.Stores
                let Type = _context.Stores.Take(1).Select(x => s.Type1).Cast<int>().FirstOrDefault()
                group Type by Type into pt1
                select new TypeTransportation
                {
                    Type = pt1.Key, // Needs to be int
                    Count = _context.Products.Where(i => i.transportId == transportId && i.Type == pt1.Key).Count()
                };            

      foreach (var item in res)
      {
          Console.WriteLine(item.Type + " " + item.Count);
      }

      Console.ReadKey();

我不能在查询语法中这样做,但是使用扩展方法语法

var products = new[]
{
    new {transportId = 5, Type = 1},
    new {transportId = 5, Type = 3},
    new {transportId = 6, Type = 3},
    new {transportId = 5, Type = 3},
    new {transportId = 5, Type = 5},
};

var stores = new[]
{
    new {Name = "Ho", Type1 = "1"},
    new {Name = "He", Type1 = "2"},
    new {Name = "Be", Type1 = "3"},
    new {Name = "Ke", Type1 = "4"},
    new {Name = "Fe", Type1 = "5"},
};

var transportId = 5;
var res = stores                    
    .GroupJoin(
        inner: products
            .Where(product =>
                product.transportId == transportId),
        innerKeySelector: product => product.Type,
        outerKeySelector: store => Int32.Parse(store.Type1),
        resultSelector: (store, storeProducts) =>
            new
            {
                StoreType = store.Type1,
                StoreName = store.Name,
                ProductsCount = storeProducts.Count()
            })
    .ToList();

foreach (var item in res)
{
    Console.WriteLine(item);
}

只需用适当的sql函数调用替换Int32.Parse以获取实际的DbContext查询代码。


使用查询语法,这可能是我能提出的最好的:

var res =
    from store in stores
    join product in 
        (from prod in products where prod.transportId == transportId select prod)
        on store.Type1 equals product.Type.ToString() into storeProducts
    select new
    {
        StoreType = store.Type1,
        StoreName = store.Name,
        ProductsCount = storeProducts.Count()
    };

基本上你需要遵循join子句(C#Reference)中描述的左连接模式。 唯一棘手的部分是pro.transportId=5条件

left Join dbo.products as pro on (sto.Type1 = pro.Type AND pro.transportId=5)

重要的是不要在连接后将其包含在where子句中。

处理它的一种可能方法是这样的:

var res = (from sto in _context.Stores
           join pro in _context.Products
           on new { sto.Type1, transportId } equals
              new { Type1 = pro.Type.ToString(), pro.transportId }
           into storeProducts
           from pro in storeProducts.DefaultIfEmpty()
           group sto by sto.Type1 into pt
           select new 
           {
               Type = pt.Key, // the string value, there is no way to convert it to int inside the SQL
               Count = pt.Count()
           }).AsEnumerable() // switch to LINQ to Objects context
           .Select(pt => new TypeTransportation()
           {
               Type = Convert.ToInt32(pt.Type), // do the conversion here
               Count = pt.Count()
           }).ToList();

或者只是where联接之前将其作为where子句应用:

var res = (from sto in _context.Stores
           join pro in _context.Products.Where(p => p.transportId == transportId)
           on sto.Type1 equals pro.Type.ToString()
           into storeProducts
           // the rest ... (same as in the first query)

另一个需要注意的细节是,为了使LEFT JOIN有效地应用,您需要按左表(在您的情况下Stores )字段(如在原始SQL查询中)进行分组,从而以string键结束。 如果您希望获取int键,则无法在db查询中执行此操作,因此您需要使用临时投影,上下文切换和最终投影,如上所示。

更新:我最初没有意识到的最后一件事是原始SQL Count(Pro.transportId)从连接的右侧排除了NULL 所以最终正确的等效LINQ查询是:

var res = (from sto in _context.Stores
           join pro in _context.MyProducts
           on new { sto.Type1, transportId } equals
              new { Type1 = pro.Type.ToString(), pro.transportId }
           into storeProducts
           from pro in storeProducts.DefaultIfEmpty()
           group new { sto, pro } by sto.Type1 into pt
           select new
           {
               Type = pt.Key,
               Count = pt.Sum(e => e.pro != null ? 1 : 0)
           })
           .AsEnumerable()
           .Select(pt => new TypeTransportation()
           {
               Type = Convert.ToInt32(pt.Type),
               Count = pt.Count
           }).ToList();

暂无
暂无

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

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