我有这个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客户端。这就是为什么我需要为它设置一个“TestID”,因为它是一个实体,它需要一个。
问题是,这个linq查询产生了这个可怕的SQL语句: http://pastebay.com/144532
我想有更好的方法可以查询这些信息,也许更好的linq表达式。或者是否可以以某种方式使用普通SQL进行查询?
修改
我意识到TestID是不必要的,因为名为“Key”的其他属性(Im分组的那个)成为组的键,所以它也是一个键。在此之后,我的查询如下所示:
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,我应该创建另一个使用LinqToSQL或其他东西运行的DomainService?
或者最好的方法是使用返回Rowsets的存储过程?如果是这样,是否有最佳实践异步执行此操作,如简单的WCF Ria DomainService查询?
答案 0 :(得分:1)
我也想了解最佳做法。
编译lambda表达式linq可能需要花费很多时间(3-30s),尤其是使用group by
然后FirstOrDefault
(对于左内连接,意味着只从组中的第一行获取值) )。
生成的sql执行可能不是那么糟糕,但使用DbContext进行编译,无法使用.NET 4.0进行预编译。
作为一个例子,例如:
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编译时间,这加起来3次= 6s仅用于编译未加载数据(花费少于500ms)。这基本上会破坏您的应用程序的可用性。用户将无缘无故地等待多次。
到目前为止,我们发现加速编译的唯一方法是将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 for DbContext。
希望这有帮助,我们可以获得其他解决方案。