用于<where nullable =“”columns =“”equals =“”> </where>的EF6 SQL生成

时间:2013-10-30 02:32:27

标签: entity-framework entity-framework-6

尝试从EF5升级到EF6,我遇到了可空列的重要性能差距搜索表。这是一个示例:

public class Customer
{
    public int Id { get; set; }
    public int? ManagerId { get; set; }
    //public virtual Manager Manager { get; set; }
}

public class MyContext : DbContext
{
    public MyContext(string connstring): base(connstring){}
    public DbSet<Customer> Customers { get; set; }
}

class Program
{
    static void Main(string[] args)
    {
        var db = new MyContext("CONNSTRING");
        var managerId = 1234;
        var q = from b in db.Customers
                where b.ManagerId == managerId
                select b.Id;
        var s = q.ToString();
    }
}

当EF6生成SQL时,它会为null处理添加一些逻辑:

SELECT 
[Extent1].[Id] AS [Id]
FROM [dbo].[Customers] AS [Extent1]
WHERE (([Extent1].[ManagerId] = @p__linq__0) 
AND ( NOT ([Extent1].[ManagerId] IS NULL OR @p__linq__0 IS NULL))) 
OR (([Extent1].[ManagerId] IS NULL) AND (@p__linq__0 IS NULL))

注意同样的linq在EF5下产生了更简单的SQL:

SELECT 
[Extent1].[Id] AS [Id]
FROM [dbo].[Customers] AS [Extent1]
WHERE [Extent1].[ManagerId] = @p__linq__0

我可以理解开发人员试图实现的要点:如果提供null作为参数,那么managerId = null的查询将不会选择任何行。我很感激这种关注,但99.9%的搜索逻辑是分开的:一个用例查找where ManagerId == null,另一个搜索特定ID where ManagerId == managerId

问题在于对性能产生重大影响:MS SQL不在ManagerId上使用索引并且发生表扫描。我的项目有数百个类似的搜索,数据库大小在升级到EF6之后大约有100GB的整体性能减少了大约10个。

问题是有人知道在EF6中禁用此包版并生成简单的sql的某种配置或约定吗?

修改

我在项目中检查了十几个类似的选择并发现:

  • 在某些情况下,SQL SERVER确实使用为字段I指定的索引 搜索。即使在这种情况下,也会出现轻微的性能损失:它 使用索引两次:第一次查找我指定的值 参数,第二次寻找null
  • 当常量被精确指定为非null时,EF6甚至会检查null,例如:

                from p in db.PtnActivations
            where p.Carrier != "ALLTEL"
            where p.Carrier != "ATT"
            where p.Carrier != "VERIZON"
    

生成SQL

    WHERE ( NOT (('ALLTEL' = [Extent1].[Carrier]) AND ([Extent1].[Carrier] IS NOT NULL))) AND ( NOT (('ATT' = [Extent1].[Carrier]) AND ([Extent1].[Carrier] IS NOT NULL))) AND ( NOT (('VERIZON' = [Extent1].[Carrier]) AND ([Extent1].[Carrier] IS NOT NULL)))

没有在载体上使用我的索引。 EF5版本

( NOT (('ALLTEL' = [Extent1].[Carrier]))) AND ( NOT (('ATT' = [Extent1].[Carrier]))) AND ( NOT (('VERIZON' = [Extent1].[Carrier]) ))

利用它。

注意条件('ALLTEL' = [Extent1].[Carrier]) AND ([Extent1].[Carrier] IS NOT NULL)。第二部分始终为false,但添加此部分会放弃索引。

我常规导入大约170万条记录(通常大约需要30分钟),持续3小时,进度大约为30%。

2 个答案:

答案 0 :(得分:14)

设置

db.Configuration.UseDatabaseNullSemantics = true;

获取您在EF5中的行为。这个workitem描述了truefalse之间的区别,并且可以帮助您确定您是否对旧行为感到满意。

答案 1 :(得分:1)

答案非常不同

如果您正在使用varchar(xxx),则LNQ to SQL会吐出nvarchar(4000),其中中断索引和转换会大大超出您的sql计划。在我的情况下,我发现这个问题是由于奇怪的空行为,但这不是问题。下面的答案解决了null和nvarchar问题。 SQL计划从~11变为.006。

public class InterestingRow
{
    [Key]
    public int interesting_row_id { get; set; }

    [StringLength(255), Required, Column(TypeName = "varchar")]
    public string public_guid { get; set; }
}

(是的,有很多理由使用varchar,就像存储公开的guid一样)