使Linq to Sql生成使用ISNULL而不是COALESCE的T-SQL

12

我有一个Linq to SQL查询,返回一些余额非零的订单(实际上,这个查询有一点复杂,但为了简单起见,我省略了一些细节)。这个查询还应该返回没有CardItems的订单(两个子查询在T-SQL中都返回NULL,比较两个NULL会得到FALSE,因此我将子查询的NULL结果值转换为0进行比较)。

var q = (from o in db.Orders
         where db.Cards(p =>
             p.OrderId == o.Id 
             && p.Sum + (db.CardItems.Where(i => i.IncomeId == p.Id)
                        .Sum(i => (double?)i.Amount) ?? 0) 
                    != (db.CardItems.Where(i => i.DeductId == p.Id)
                        .Sum(i => (double?)i.Amount) ?? 0)
            ).Any()
         select o);

问题是,将表达式 Sum(i => (double?)i.Amount) ?? 0 转换为 T-SQL 查询语句时使用 COALESCE 运算符,但由于其中的子查询,它比替换为 ISNULL 的完全相同的 T-SQL 查询慢十倍。在这种情况下是否有可能生成 ISNULL 呢?


4
@PreetSangha,如果您知道问题在哪里,以正确的方式编写代码有什么“错误”吗?如果您明知问题所在却不采取任何措施,那么我认为这是错误的方法。如果您故意制作一个比可能更慢10倍的代码,那么将优化称为“错误”是相当有趣的... - walther
2
@walther:许多人已经说过为什么过早的优化是有害的。OP:查询的总运行时间是否受到显着影响?这应该指导您是否应该关注此事,而不是您以前对运算符的经验。 - Guvante
1
@Guvante和许多其他人已经说过,用“我以后可能会修复它”的话故意编写糟糕的代码是一种不好的方法...如果你知道你的代码很糟糕,就不要等到它成为问题...这只是我的观点,也许你喜欢以后再修复它 :) - walther
3
@walther:除非存在实际的性能问题,否则没有什么需要修复的。我没有看到任何实际的性能测试来表明COALESCE会给他带来问题,只是他说听说这样做不好。无论哪种情况,他都不应该像Oblivion2000的答案所示那样做任何事情,因此这是一个无意义的论点。 - Guvante
6
各位,楼主已经清楚地表明在他的情况下ISNULL是更可取的。要么提供一个解决方法,要么回答问题!不需要进一步争论。 - usr
显示剩余16条评论
2个回答

2
根据我的经验,让Linq生成你想要的SQL最多是一件麻烦的事情。如果你有一个比Linq更好的查询实现(能正常工作且性能良好),那就使用它吧,即使只是为了这一个查询。
最快的方法可能是使用DataContext.ExecuteQuery<TResult>。它甚至会为你填充和处理返回的对象,就像Linq自动生成查询一样。
很多人更愿意将此SQL放入存储过程中,特别是在生产代码中。然后你只需要将存储过程映射到你的Linq对象中,它就可以正常工作了。正如通常情况下所说的,ScottGu在他的博客中详细介绍了如何做到这一点:

0

既然您假设没有金额的行应该被视为零进行求和,那么您可以过滤掉没有金额的行,不必担心IsNull或coalesce。

&& p.Sum + (db.CardItems.Where(i => i.IncomeId == p.Id)
                    .Where(i=> i.Amount > 0)
                    .Sum(i => (double?)i.Amount) ?? 0) 
                != (db.CardItems.Where(i => i.DeductId == p.Id)
                    .Where(i=> i.Amount > 0)
                    .Sum(i => (double?)i.Amount) ?? 0)

由于我不知道你的对象,你甚至可以删除强制转换(double?)和默认(??)操作符


如果删除空合并 ?? 运算符,您的查询将不会返回没有 CardItems 的订单(SUM 运算符将返回 NULL,与 NULL 的比较始终返回 FALSE),它将等同于 && p.Sum + (db.CardItems.Where(i => i.IncomeId == p.Id).Sum(i => i.Amount)) != (db.CardItems.Where(i => i.DeductId == p.Id).Sum(i => i.Amount)。如果保留 ?? 在原地,它将不会带来任何优势,因为生成的 T-SQL 几乎与原始的相同。问题不在于 NULL Amount,这一列甚至不可为空,而在于 NULL SUM。 - dmitry.s

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接