简体   繁体   English

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

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

I need help with doing a Left join in a linq statement. 我需要帮助在linq语句中执行左连接。 My T-sql query works as expected but I can't seem to get the wanted results from the Linq. 我的T-sql查询按预期工作,但我似乎无法从Linq获得想要的结果。 I also realize that there are ton of questions like mine, but I can't seem to apply any of the solutions to my case. 我也意识到像我这样的问题很多,但我似乎无法对我的案例应用任何解决方案。

Products table 产品表

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

Stores 商店

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

My wanted result is 我想要的结果是

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

My tsql that works as intended 我的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

My Linq attempt returns this. 我的Linq尝试返回此。

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

Linq Statement. 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();

I've tried doing some defaultifempty but can't seem to make it work. 我试过做一些defaultifempty但似乎无法使它工作。

Here is MSDN link "How to: Perform Left Outer Joins" with LINQ: https://msdn.microsoft.com/en-gb/library/bb397895.aspx 这是MSDN链接“如何:执行左外连接”与LINQ: https//msdn.microsoft.com/en-gb/library/bb397895.aspx

You code should be like this: 你的代码应该是这样的:

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

Wow .. lastly i did it .. ..最后我做到了..

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

I can't do it in query syntax, but using extension method syntax it will be 我不能在查询语法中这样做,但是使用扩展方法语法

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

Just replace Int32.Parse with appropriate sql function call for actual DbContext query code. 只需用适当的sql函数调用替换Int32.Parse以获取实际的DbContext查询代码。


With query syntax this is probably the best I can propose: 使用查询语法,这可能是我能提出的最好的:

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

Basically you need to follow the left join pattern described in join clause (C# Reference) . 基本上你需要遵循join子句(C#Reference)中描述的左连接模式。 The only tricky part is the pro.transportId=5 condition in 唯一棘手的部分是pro.transportId=5条件

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

The important thing is to not include it as where clause after the join. 重要的是不要在连接后将其包含在where子句中。

One possible way to handle it is like this: 处理它的一种可能方法是这样的:

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

or just apply it as where clause before the join: 或者只是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)

Another detail to mention is that in order to make LEFT JOIN effectively apply, you need to group by the left table ( Stores in your case) field (like in the original SQL query), thus ending up with a string key. 另一个需要注意的细节是,为了使LEFT JOIN有效地应用,您需要按左表(在您的情况下Stores )字段(如在原始SQL查询中)进行分组,从而以string键结束。 If you wish to get the int key, there is no way to do it inside the db query, so you need to use a temporary projection, context switch and the final projection as shown above. 如果您希望获取int键,则无法在db查询中执行此操作,因此您需要使用临时投影,上下文切换和最终投影,如上所示。

UPDATE: The last thing that I didn't realize initially is that the original SQL Count(Pro.transportId) is excluding NULL s from the right side of the join. 更新:我最初没有意识到的最后一件事是原始SQL Count(Pro.transportId)从连接的右侧排除了NULL So the final correct equivalent LINQ query is: 所以最终正确的等效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