从 LINQ 生成的 SQL 不一致
Posted
技术标签:
【中文标题】从 LINQ 生成的 SQL 不一致【英文标题】:SQL generated from LINQ not consistent 【发布时间】:2016-11-30 15:54:47 【问题描述】:我正在针对 ORACLE 使用 Telerik Open/Data Access ORM。
为什么这两条语句会导致不同的 SQL 命令?
声明 #1
IQueryable<WITransmits> query = from wiTransmits in uow.DbContext.StatusMessages
select wiTransmits;
query = query.Where(e=>e.MessageID == id);
以下 SQL 的结果
SELECT
a."MESSAGE_ID" COL1,
-- additional fields
FROM "XFE_REP"."WI_TRANSMITS" a
WHERE
a."MESSAGE_ID" = :p0
声明 #2
IQueryable<WITransmits> query = from wiTransmits in uow.DbContext.StatusMessages
select new WITransmits
MessageID = wiTranmits.MessageID,
Name = wiTransmits.Name
;
query = query.Where(e=>e.MessageID == id);
以下 SQL 的结果
SELECT
a."MESSAGE_ID" COL1,
-- additional fields
FROM "XFE_REP"."WI_TRANSMITS" a
使用第二条语句#2 生成的查询返回,显然当我只想要一条时,表中的每条记录。数以百万计的记录使这令人望而却步。
【问题讨论】:
看起来投影被 ORM 返回为IEnumerable
。在第一个语句之后的两种情况下query
的类型是什么?
它们都是 IQueryablesql
。
【参考方案1】:
Telerik Data Access 将尝试将每个查询拆分为数据库端和客户端(或内存中的 LINQ,如果您愿意的话)。
使用select new
进行投影肯定会触发,这将使投影后 LINQ 表达式树中的所有内容都转到客户端。
这意味着在您的第二种情况下,您的 LINQ 查询效率低下,因为任何过滤都是在内存中应用的,并且您已经传输了许多不必要的数据。
如果您想以案例 2 中的方式编写 LINQ 表达式,您可以在最后附加 Select
子句或将结果显式转换为 IEnumerable<T>
以明确任何进一步的处理都将在内存中完成。
【讨论】:
【参考方案2】:第一个查询返回定义的完整对象,因此可以在实际运行之前将任何其他限制(如Where
)附加到它。因此,可以按照您的说明组合查询。
第二个返回一个新对象,它可以是任何类型并包含任何信息。因此,查询以“返回所有内容”的形式发送到数据库,并且在创建对象后,除了与 Where
子句匹配的对象之外的所有对象都将被丢弃。
即使两者的类型相同,想想这种情况:
var query = from wiTransmits in uow.DbContext.StatusMessages
select new WITransmits
MessageID = wiTranmits.MessageID * 4 - 2,
Name = wiTransmits.Name
;
您现在如何组合Where
查询?当然,您可以检查新对象创建内部的代码并尝试将其移到外部,但由于可能存在 anything,因此这是不可行的。如果检查是一些查找功能怎么办?如果它不是确定性的怎么办?
因此,如果您基于数据库对象创建新对象,则会有一个边界,将检索对象,然后将在内存中进行进一步的查询。
【讨论】:
在像实体框架这样的 ORM 中,整个表达式(包括wiTranmits.MessageID * 4 - 2
)将被翻译成 SQL,因此按 MessageID
进行排序/过滤也将显示在查询中。我认为 Telerik 在这里有不同的策略。
也许更好的检查是 MessageID = LocalFunction(wiTransmits.MessageID),以上是关于从 LINQ 生成的 SQL 不一致的主要内容,如果未能解决你的问题,请参考以下文章
SQL Server 管理对象 (SMO) 的默认约束不一致