EF6 SQL生成<where nullable columns equals>

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處理添加一些邏輯:

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();
    }
}

注意同樣的linq在EF5下產生了更簡單的SQL:

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();
    }
}

我可以理解開發人員試圖實現的要點:如果提供null作為參數,那麼managerId = null的查詢將不會選擇任何行。我非常感謝關注,但99.9%的搜索邏輯是分開的:一個用例查找where ManagerId == null ,另一個用於查找特定ID where ManagerId == managerId

問題在於性能影響很大:MS SQL不使用ManagerId上的索引並發生表掃描。我的項目有數百個類似的搜索和數據庫大小約100GB的整體性能升級到EF6減少約10。

問題是,是否有人知道某種配置或慣例在EF6中禁用此障礙並生成簡單的sql?

編輯:

我在我的項目中檢查了十幾個類似的選擇,發現:

  • 在某些情況下,SQL SERVER確實使用為我搜索的字段指定的索引。即使在這種情況下也會有輕微的性能損失:它使用索引兩次:第一次查找我在參數中指定的值,第二次查找null
  • 當常量被精確指定為非null時,EF6甚至會檢查null,例如:

    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();
        }
    }
    

生成SQL

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();
    }
}

沒有在載體上使用我的索引。 EF5版本了

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();
    }
}

利用它。

注意條件('ALLTEL' = [Extent1].[Carrier]) AND ([Extent1].[Carrier] IS NOT NULL) 。第二部分總是假的,但添加這部分會放棄索引。

我常規導入大約170萬條記錄(通常大約需要30分鐘),持續3小時,進度大約為30%。

一般承認的答案

db.Configuration.UseDatabaseNullSemantics = true;

獲得你在EF5中的行為。這個工作項描述了truefalse之間的區別是什麼,應該幫助你決定你是否對舊的行為感到滿意。


熱門答案

答案非常不同

如果您使用的是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)




許可下: CC-BY-SA with attribution
不隸屬於 Stack Overflow
這個KB合法嗎? 是的,了解原因
許可下: CC-BY-SA with attribution
不隸屬於 Stack Overflow
這個KB合法嗎? 是的,了解原因