实体框架 - 荒谬的查询,将 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 进行比较 [重复]的主要内容,如果未能解决你的问题,请参考以下文章

将 PostgreSQL 查询转换为实体框架

《Entity Framework 6 Recipes》中文翻译系列 (12) -----第三章 查询之使用SQL语句 (转)

实体框架 ChangeTracker 流并保存到查询

添加新导航属性后,实体框架在查询时尝试将属性设置为 null

将 POCO/实体添加到 DbContext 以进行自定义查询/过程,而无需先在实体框架代码中创建表

实体框架:查询子实体 [重复]