实体框架 - 荒谬的查询,将 smallint 转换为 int 进行比较 [重复]
Posted
技术标签:
【中文标题】实体框架 - 荒谬的查询,将 smallint 转换为 int 进行比较 [重复]【英文标题】:Entity Framework - ridiculous Query, casting smallint to int for comparison [duplicate] 【发布时间】:2014-03-03 14:35:17 【问题描述】:这里没有想法。我有一个简单的表,它是模型首先使用实体框架映射的,并且生成了以下 SQL:
(@p__linq__0 int,@p__linq__1 int)SELECT
[Extent1].[BucketRef] AS [BucketRef],
[Extent1].[VariantNo] AS [VariantNo],
[Extent1].[SliceNo] AS [SliceNo],
[Extent1].[TradeNo] AS [TradeNo],
[Extent1].[TradeBegin] AS [TradeBegin],
[Extent1].[TradeEnd] AS [TradeEnd],
FROM [simstg].[Trade] AS [Extent1]
WHERE ((( CAST( [Extent1].[BucketRef] AS int) = @p__linq__0) AND ( NOT (( CAST( [Extent1].[BucketRef] AS int) IS NULL) OR (@p__linq__0 IS NULL)))) OR (( CAST( [Extent1].[BucketRef] AS int) IS NULL) AND (@p__linq__0 IS NULL))) AND ((( CAST( [Extent1].[VariantNo] AS int) = @p__linq__1) AND ( NOT (( CAST( [Extent1].[VariantNo] AS int) IS NULL) OR (@p__linq__1 IS NULL)))) OR (( CAST( [Extent1].[VariantNo] AS int) IS NULL) AND (@p__linq__1 IS NULL)))
所有这些演员都会扼杀表演。很遗憾,我确实看不到它们来自哪里。
有问题的查询是:
var tradesQuery = repository.SimStgTrade
.Where(x => x.BucketRef == bucketId && x.VariantNo == set)
.ToArray();
这很简单。字段定义为:bucketId:short(数据库中的smallint),设置short,数据库中的smallint。因此,完全不需要演员表。我已经在模型中删除并重新创建了表——据我所知,映射匹配(字段为 smallint)。因此,我们遇到了严重的性能问题 - 例如:查询超时,因为它没有使用表扫描。
有人知道如何摆脱这些演员表并强制比较基于短裤吗?从 SQL 中可以很明显地看出,EF 决定首先将所有内容移至 int ......这没有任何意义。
这不是“它很好”的事情。未完成的查询路径完全不同,生成的代码将其转换为自连接。在服务器管理器中,EF 变体需要 5 分钟以上,而使用简单 SQL 的优化版本需要 0.0 秒(从该表的数十亿中返回 228 行)。
【问题讨论】:
投票关闭自己 - 这与***.com/questions/9016265/… 的问题类似 - 仅针对 smallint。我不会删除它,因为我认为它应该作为参考。这是一个简单的解决方法,效果很好。 您是否尝试过将模型更改为使用 Int 而不是 Short 来查看它是否会移除强制转换。在这种情况下,您为更小的内存占用而获得的任何优化都会在数据库访问中丢失。 不,我没有。该模型有一个短路,因为它是一个短路。该表目前有 100 亿个条目,多 2 个字节将导致更多数据。正是出于这个原因,我们优化了空间使用。这是一个每天输入 50 亿行的表格。我们在键是 int 的其他表上存在问题 - 这对于 smallint/tinyint 来说是特别的。而当表是数百 GB 时,数据库空间是相当残酷的。 228 行的差异是 5 分钟 15 到不到一秒。根据我的链接,解决方法已确认并已投入生产。 【参考方案1】:由于 C# 编译器如何为您的 Where 表达式生成表达式树,这种行为对于不同的 LINQ 提供程序可能很常见,并且不仅特定于 EF。
当你指定条件为:
.Where(x => x.BucketRef == bucketId)
并且 BucketRef 和 bucketId 都是短裤,编译器为两个比较部分生成从 short 到 int 的强制转换,因为 == 运算符没有为 Short 类型定义。这在回答 https://***.com/a/18584429/869184
中有解释作为一种解决方法,您可以通过以下方式重写条件:
.Where(x => x.BucketRef.Equals(bucketId))
这有效地消除了比较中的演员表。
【讨论】:
【参考方案2】:您需要使用Expression 类中的静态函数自己创建Where
表达式。
像这样:
Int16 bucketId = 3;
var parameter = Expression.Parameter(typeof(SimStgTrade));
var property = Expression.PropertyOrField(parameter, "BucketRef");
var constant = Expression.Constant(bucketId);
var comparison = Expression.Equal(property, constant);
var lambda = Expression.Lambda<Func<SimStgTrade,bool>>(comparison, parameter);
var tradesQuery = repository.SimStgTrade
.Where(lambda)
.Where(x => x.VariantNo == set)
.ToArray();
对VariantNo == set
执行相同操作,以便同时删除该演员表
【讨论】:
实际上有一个更简单的解决方法。 var bucketsfilter = new [] bucketId ;然后 .Where(x => bucketsfilter.Contains(x.BucketRef) - 也没有。在 ***.com/questions/9016265/… 上找到它,同样适用于 smallint。零质量控制 - 很明显。 这对于进行 Equal 以外的其他比较非常有帮助。例如,只需将 Expression.Equal 替换为 Expression.GreaterThan 或 Expression.Less。我的 SQL 查询不再包含不需要的“强制转换”操作。以上是关于实体框架 - 荒谬的查询,将 smallint 转换为 int 进行比较 [重复]的主要内容,如果未能解决你的问题,请参考以下文章
《Entity Framework 6 Recipes》中文翻译系列 (12) -----第三章 查询之使用SQL语句 (转)
添加新导航属性后,实体框架在查询时尝试将属性设置为 null