【问题标题】:How to transform grouping with MAX from sql to linq query如何将使用 MAX 的分组从 sql 转换为 linq 查询
【发布时间】:2021-03-15 10:53:00
【问题描述】:

我一直在尝试将下面的 sql 查询转换为 linq:

SELECT
    Table1.Id1,
    Table2.Id2,
    MAX(Table3.DateField) AS MaxDateField
FROM
    Table1 
INNER JOIN
    Table2 ON Table1.Id1 = Table2.FK1 
LEFT OUTER JOIN
    Table3 ON Table2.Id2 = Table3.FK2
GROUP BY 
    Table1.Id1, Table2.Id2

Table1/Table2的关系是一对多,Table2/Table3也是一对多。

SQL 工作正常,对于 Table1 和 Table2 的每条记录,只提供来自 Table3 的一条记录。

对于我尝试过的 linq:

var query = from table1 in tables1
            join table2 in tables2 on table1.Id1 equals table2.FK1
            join table3 in tables3 on table2.Id2 equals table3.FK2 into JoinedTable3Table2
            from table3 in JoinedTable3Table2.DefaultIfEmpty()

            group new {
                 table3.MyDateField
            }
            by new  {
                 table1.Id1,
                 table2.Id2,
                 table3.MyDateField
            }
            into g    
            select new MyModel {
                 Field1 = g.Key.Id1,
                 Field2 = g.Key.Id2,
                 Field3 = g.Max(d => d.MyDateField)
            };

但这会为 Table1 和 Table2 中的每条记录从 Table3 中返回 n 条记录。

然后我尝试了(正如 Svyatoslav Danyliv 建议的那样):

var query = from table1 in tables1
            join table2 in tables2 on table1.Id1 equals table2.FK1
            join table3 in tables3 on table2.Id2 equals table3.FK2 into JoinedTable3Table2
            from table3 in JoinedTable3Table2.DefaultIfEmpty()

            group new {
                 table1.Id1,
                 table2.Id2
            }
            by new  {
                 table1.Id1,
                 table2.Id2,
                 table3.MyDateField
            }
            into g    
            select new MyModel {
                 Field1 = g.Key.Id1,
                 Field2 = g.Key.Id2,
                 Field3 = g.Max(d => d.MyDateField) 
            };

然后我在Field3 = g.Max(d => d.MyDateField) 收到错误(CS 1061:匿名类型)

【问题讨论】:

    标签: c# sql sql-server linq


    【解决方案1】:

    您在分组字段方面存在差异table3.MyDateField 是错误的。 此外,如果您的模型具有适当的导航属性,则可以简化您的查询。

    var query = from table1 in tables1
                from table2 in table1.Table2s 
                from table3 in table2.Table3s 
    
                group table3
                by new  {
                     table1.Id1,
                     table2.Id2
                }
                into g    
                select new MyModel {
                     Field1 = g.Key.Id1,
                     Field2 = g.Key.Id2,
                     Field3 = g.Max(d => d.MyDateField)
                };
    

    【讨论】:

    • 我有 Table1、Table2 和 Table3 的导航属性,您的方法有效。但我的示例是简化版本。我还需要通过“左外连接”(即 ...DefaultIfEmpty())将一些额外的表加入到 table2 中,以便从这些表中获取属性。如何使用您的方法加入这些额外的表格?
    • 是的,您可以进行连接。您只是使用了错误的 GroupBy 键。
    • 是的,我更正了错误。但是我需要对没有设置导航属性的表进行额外的连接(参见我上面的评论)。我将如何加入这些表?
    • 我没有遇到问题。像您原来的问题一样添加连接。它是正确的。如果没有导航属性 - 使用 join 运算符。
    • 检查更新的问题。 group 错了,by 错了。检查我的示例如何分组
    【解决方案2】:

    如果您的模型定义正确,则您实际上不需要在 LINQ 中进行连接。

    看看这个查询是否有效

    var results = context.tables2.Select(row => new 
            { row.FK1, row.ID2, MaxDate = row.LinkedEntities.Max(e => e.MyDateField) }));
    

    这将要求您将 LinkedEntities 定义为表 2 和表 3 之间 1-* 关系的导航属性。

    【讨论】:

    • “如果您的模型定义正确”是什么意思?我正在通过 UnitOfWork 获取实体的 api 控制器上的 mvc 项目工作。控制器中使用的模型是一个没有导航属性的 C# 类,只需 { get;放; } 声明的属性。
    • 我应该说“完全”而不是正确。 :) 没有正确导航属性和 FK 的 EF,以及具有显式连接的 LINQ,Include()s 和 group by 在大多数情况下表明 EF 提供的功能集没有被完全使用。
    • 如果您指的是数据库模型:已定义导航属性,例如 Table1.Tables2 ...
    • 我让它从 Table1、Table2 和 Table3 中收集记录。但是我需要我必须加入的其他 2 个表中的一些其他属性,但没有导航属性。我确实有制作“左外连接”的钥匙。我怎样才能做到这一点?
    • 有什么阻止您添加所需的导航属性吗?这是 imo 最干净的方式。
    【解决方案3】:

    感谢 Badar 和 Danyliv 的帮助,我找到了可行的解决方案。

    我需要提及的是,我发布的示例是一个简化版本。我还需要通过“左外连接”将 table2 连接到第 4 个表(Table4),以便从 Table4(未设置导航属性)获取属性,但匹配键可用。

    var query = from table1 in tables1
                join table2 in tables2 on table1.Id1 equals table2.FK1
    
                join table4 in tables4 on table2.Id2 equals table4.FK2 into JoinedTable4Table2
                from table4 in JoinedTable4Table2.DefaultIfEmpty()
    
                join table3 in tables3 on table2.Id2 equals table3.FK2 into JoinedTable3Table2
                from table3 in JoinedTable3Table2.DefaultIfEmpty()
    
                group table3 // needed to be changed
                by new {
                     table1.Id1,
                     table2.Id2,
                     table4.Id4,
                     // any other field, but no field from table3
                     // table3.MyDateField // had to be removed!!
                }
                into g    
                select new MyModel {
                     Field1 = g.Key.Id1,
                     Field2 = g.Key.Id2,
                     Field4 = g.Key.Id4,
                     Field3 = g.Max(d => d.MyDateField)
                     //... 
                };
    

    【讨论】:

      猜你喜欢
      • 2017-04-12
      • 1970-01-01
      • 2020-07-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-10-26
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多