与硬编码整数值相比,使用整数变量时 Linq to SQL 查询非常慢

A Linq to SQL query is very slow when using an interger variable compared to a hardcoded integer value

我有一个查询大约需要 10 秒才能执行。

奇怪的是,如果我用硬编码整数(例如 3)替换 "machineNo" 参数,查询需要一瞬间。这是我迄今为止遇到的最不寻常的性能问题。

public static IEnumerable<vwJobAppointment> GetAllJobs(int machineNo)
{
    var db = new DbContext();
    IEnumerable<vwJobAppointment> list;
    list = db.vwJobAppointments.Where(a => a.ResourceId == (machineNo)).AsNoTracking().ToList();
    return list;
}

查询需要 10 秒 +

public static IEnumerable<vwJobAppointment> GetAllJobs(int machineNo)
{
    var db = new DbContext();
    IEnumerable<vwJobAppointment> list;
    list = db.vwJobAppointments.Where(a => a.ResourceId == (3)).AsNoTracking().ToList();
    return list;
}

这个查询不到一秒钟。

有什么想法吗?我正在使用 Entity Framework 5 和 SQL 2008 数据库

这听起来像是参数嗅探;即典型的参数化计划缓存问题,其中查询计划是使用 initial 示例参数值生成的,结果需要一个与其他一些值截然不同的计划。如果数据有偏差,这很常见 - 例如,某些值会有 3 个匹配项,而其他值会有 300,000 个匹配项。这通常是在 OPTION (OPTIMIZE FOR ... UNKNOWN) 提示发挥作用时,但这需要 您的 ORM 能够指定此提示, 让你手写SQL。

EF 查询被翻译成 SQL。 SQL 使用查询计划来优化查询。变量往往会使程序变慢很多。在某些情况下,如果您 select 临时 table 中的输入变量并加入此 table 您通常会检查输入变量的地方,则可以克服此问题。该过程的另一个好处是您可以强制执行查询计划。我建议:

  • 创建一个将资源 ID 作为输入的存储过程
  • 尝试 select 在临时 table 中输入变量并加入那个
  • 如果这对强制执行查询计划没有帮助

您可以稍微优化一下您的 where 表达式。在这种情况下,您可以将参数变量重新分配给局部变量。在我的测试中,我有三种方法:

// Execution time: 00:00:00.0140118
public static IEnumerable<myClass> GetAllJobs(int machineNo)
{
    var db = new DbContext();
    IEnumerable<myClass> list;
    list = db.vwJobAppointments.Where(a => a.ResourceId == (machineNo)).ToList();
    return list;
}

// Execution time: 00:00:00.0019991
public static IEnumerable<myClass> GetAllJobs2(int machineNo)
{
    var db = new DbContext();
    IEnumerable<myClass> list;
    list = db.vwJobAppointments.Where(a => a.ResourceId == 55).ToList();
    return list;
}

// Execution time: 00:00:00.0010013
public static IEnumerable<myClass> GetAllJobs3(int machineNo)
{
    int machineNo2 = machineNo;
    var db = new DbContext();
    IEnumerable<myClass> list;
    list = db.vwJobAppointments.Where(a => a.ResourceId == (machineNo2)).ToList();
    return list;
}