【问题标题】:Linq Left join, where, group by, count()Linq Left join, where, group by, count()
【发布时间】:2016-03-08 09:37:16
【问题描述】:

我需要在 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 但似乎无法使其工作。

【问题讨论】:

  • 将连接更改为左连接不起作用?
  • 您的预期输出中的第三行不应该是| 3 | 3 | 2 ??
  • linq 中没有左连接
  • Utsav,不,因为我一次只搜索一个特定的 transportId。在这种情况下为 5。因此只有 1 类型为 3。

标签: c# sql linq


【解决方案1】:

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

【讨论】:

    【解决方案2】:

    ..我终于做到了..

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

    【讨论】:

    • 怎么样??在什么情况下?
    • @user56567675 为什么要删除您的 cmets?无论如何,让我知道您在上述查询中遇到了什么问题?
    【解决方案3】:

    我不能在查询语法中做到这一点,但使用扩展方法语法它将是

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

    只需将Int32.Parse 替换为实际 DbContext 查询代码的适当 sql 函数调用即可。


    对于查询语法,这可能是我能提出的最好的建议:

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

    【讨论】:

    • 我无法找到与 EF 一起使用的等效 int 字符串。
    • @user56567675 所以只要反其道而行之,就像最初的尝试一样(int to string)。
    【解决方案4】:

    基本上你需要遵循join clause (C# Reference) 中描述的left join 模式。唯一棘手的部分是

    中的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 子句加入之前应用:

    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)NULLs 排除在连接的右侧。所以最终正确的等效 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();
    

    【讨论】:

    • 仍然没有工作,最终得到了所有类型,这很棒,并且存在类型的正确值。但是那些应该是 0 的东西到处都是,大多数是 1 其他是一些随机值。
    • @user56567675 你说得对,我漏掉了一个细节。尝试更新的答案(因为我不想考虑当前接受的答案会生成什么 SQL)。
    猜你喜欢
    • 1970-01-01
    • 2020-05-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-09-09
    • 1970-01-01
    相关资源
    最近更新 更多