如何简化这个LINQ实体查询,使一个不那么可怕的SQL语句?(包含Distinct,GroupBy和Count)



我有这个SQL表达式:

SELECT Musclegroups.Name, COUNT(DISTINCT Workouts.WorkoutID) AS Expr1
FROM   Workouts INNER JOIN
       Series ON Workouts.WorkoutID = Series.WorkoutID INNER JOIN
       Exercises ON Series.ExerciseID = Exercises.ExerciseID INNER JOIN
       Musclegroups ON Musclegroups.MusclegroupID = Exercises.MusclegroupID
GROUP BY Musclegroups.Name

因为我在一个WCF Ria LinqToEntitiesDomainService中使用EF的项目上工作,我必须用LINQ查询这个(如果这不是必须的,那么请通知我)。我做了这样的表达:

var WorkoutCountPerMusclegroup = (from s in ObjectContext.Series1
                                 join w in ObjectContext.Workouts on s.WorkoutID equals w.WorkoutID
                                 where w.UserID.Equals(userid) && w.Type.Equals("WeightLifting")
                                 group s by s.Exercise.Musclegroup into g                                              
                                 select new StringKeyIntValuePair
                                 {
                                      TestID = g.Select(n => n.Exercise.MusclegroupID).FirstOrDefault(),                                                  
                                      Key = g.Select(n => n.Exercise.Musclegroup.Name).FirstOrDefault(),
                                      Value = g.Select(n => n.WorkoutID).Distinct().Count()
                                 });

StringKeyIntValuePair只是一个自定义的实体类型,所以我可以把信息发送到Silverlight客户端。这也是为什么我需要为它设置一个"testd",因为它是一个实体,它需要一个。

问题是,这个linq查询产生了这个可怕的SQL语句:http://pastebay.com/144532

我想有更好的方法来查询这些信息,也许是一个更好的linq表达式。还是可以用普通的SQL查询?

编辑:

我意识到testd是不必要的,因为另一个名为"Key"的属性(我在其上进行分组)成为组的键,所以它也将是一个键。在这之后,我的查询看起来像这样:

var WorkoutCountPerMusclegroup = (from s in ObjectContext.Series1
                                          join w in ObjectContext.Workouts on s.WorkoutID equals w.WorkoutID
                                          where w.UserID.Equals(userid) && w.Type.Equals("WeightLifting")
                                          group w.WorkoutID by s.Exercise.Musclegroup.Name into g                                              
                                          select new StringKeyIntValuePair
                                          {                                                                                                   
                                              Key = g.Key,
                                              Value = g.Select(n => n).Distinct().Count()
                                          });

生成如下SQL: http://pastebay.com/144545

这似乎比前一个半生不熟的linq查询的sql语句要好得多。但这就足够好了吗?或者这是LinqToEntities能力的边界,如果我想要更清晰的sql,我应该做另一个DomainService与LinqToSQL或其他东西?

或者最好的方法是使用存储过程,返回行集?如果是这样,是否有最佳实践来异步完成此操作,如简单的WCF Ria DomainService查询?

我也想知道最好的做法。

编译lambda表达式linq可能会花费很多时间(3 - 30秒),特别是使用group byFirstOrDefault(对于左内连接意味着只取组中第一行的值)。

生成的sql执行可能没有那么糟糕,但是使用。net 4.0无法预编译的DbContext进行编译。

例如:

 var q = from a in DbContext.A
     join b ... into bb from b in bb.DefaultIfEmtpy()
     group new { a, b } by new { ... } into g
     select new 
     { 
        g.Key.Name1
        g.Sum(p => p.b.Salary)
        g.FirstOrDefault().b.SomeDate
     };

在一种情况下,我们添加的每个FirstOrDefault导致了+2s的编译时间,而在编译而不是加载数据(这需要少于500ms)的情况下,这些时间加起来是3倍= 6s。这基本上破坏了应用程序的可用性。用户将无缘无故地等待多次。

到目前为止,我们发现加快编译速度的唯一方法是将lambda表达式与对象表达式混合使用(可能不是正确的表法)。

示例2:重构前一个示例1。

var q = (from a in DbContext.A
     join b ... into bb from b in bb.DefaultIfEmtpy()
     select new { a, b })
     .GroupBy(p => new { ... })
     .Select(g => new 
        { 
           g.Key.Name1
           g.Sum(p => p.b.Salary)
           g.FirstOrDefault().b.SomeDate
        });

在我们的例子中,上面的例子比例1编译得快得多,但仍然不够快,所以在响应关键领域,我们唯一的解决方案是恢复到本地SQL(到实体)或使用视图或存储过程(在我们的例子中是Oracle PL/SQL)。

一旦我们有时间,我们将测试预编译是否可以在。net 4.5和/或。net 5.0中为DbContext工作。

最新更新