2017-10-06 169 views
0

我有连接使用该条件的表的查询:ISNULL与“IS NULL和字段= 0”

(Apps.mfrId = Manufacturer.id OR Apps.mfrId IS NULL OR Apps.mfrId = 0) 

查询需要17秒至运行,并且使用分析的查询导致5之间 - 30条消息(它会改变每次运行),错误为“错误:1222,严重性:16,状态:18”。

我离开查询完全一样,但我改变上述条件为:

(Apps.mfrId = Manufacturer.id OR ISNULL(apps.mfrId, 0) = 0) 

...现在这一个变化相同的查询在140ms的运行,并没有锁定的错误。

为什么会发生这种情况?

请注意,在对表制造商和应用程序进行测试之前,我已经运行带有repair_bebuild选项的DBCC CHECKTABLE,并且还重建了两个表上的索引。

另请注意,没有其他查询在同一时间对数据库运行。

这里是一个有错误的查询的简化版本:

select top 2000 Apps.object_id 
from 
    Manufacturer 
    INNER JOIN Apps ON (
     Apps.mfrId = Manufacturer.Id 
     OR Apps.mfrId IS NULL 
     OR Apps.mfrId = 0 
    ) 
where 
    Apps.OBJECT_ID = 6879149 

如果不是“顶2000”我使用“1000强”的查询,在短短大于100ms完成。

+0

您是否显示每个查询的执行计划? –

+0

连接条件看起来非常奇怪。这是一个左连接,内连接,?我感兴趣的是为什么你想要一个连接条件,如果apps.mfrid为空将被覆盖。听起来像你只是想完全加入。什么是最终目标? – scsimon

+0

@scsimon这是一个内部连接。我正在使用我没有设计的桌子。 Apps.mfrId是Manufacturer.id的空(null)外键(没有外键约束设置)。出于某种原因,原始设计允许NULL或0,并且NULL或0具有相同的含义(即没有设置有效值)。 –

回答

0

如果Apps.mfrId为null,那么您将加入每个制造商。

从您的测试, “OR ISNULL(apps.mfrId,0)= 0” 确实比这个更便宜的 “OR Apps.mfrId为空或Apps.mfrId = 0”

这些做似乎很等价的,你已经有了一个解决方案,所以问题是为什么和/或如何让非表演者工作。

说到这样的表现,如果你说的一切都是正确的,当我们开始问为什么时,我们开始指向查询优化器。如果它对于相同的参数表现不同,它将有不同的查询计划。您可能会看到表扫描而不是索引使用情况,或者对性能不佳的其他解释。

我鼓励你比较一下查询计划,或者让别人来帮助引入一个关于它在做什么不同的答案。但是,你已经可以告诉它正在做一些不同的事情。

一种可能性是,当查询优化器修复被释放,他们在默认情况下不开启,除非标志4199(所有修正或其他标志的具体修正)被开启。这是因为一般修补程序对于大多数人来说可能是好的,但是也可能会破坏在现有的怪癖环境中进行了优化的应用程序。

https://dba.stackexchange.com/questions/102292/trace-flag-4199-enable-globally

是否在帮助开启4199?

select top 2000 Apps.object_id 
from 
    Manufacturer 
    INNER JOIN Apps ON (
     Apps.mfrId = Manufacturer.Id 
     OR Apps.mfrId IS NULL 
     OR Apps.mfrId = 0 
    ) 
where 
    Apps.OBJECT_ID = 6879149 
OPTION(QUERYTRACEON 4199) 

另一个相关的议题是参数嗅探。有时候查询计划可能会被缓存,这对于一个参数是最佳的,但是对于另一个参数是可怕的。在你的情况下,一个应用程序可能会返回1个制造商,但另一个应用程序可能会返回所有制造商,这就是值得一提的原因。这通常显示为相同的代码在给定不同参数时不一致地执行不良。 您可以尝试关闭参数嗅探或强制重新编译以帮助诊断问题是否成为问题的一部分。

What are the main differences between OPTION(OPTIMIZE FOR UNKNOWN) and OPTION(RECOMPILE)?

我曾经见过,如果觉得查询优化器的情况下已经放弃细读指标,只是因为该语句变得过于复杂。但在你的例子中这似乎不太可能;除了查询优化器错误的问题得到解决之外,要利用它们,您仍然必须打开查询标记,否则您安装的补丁可能无法做任何事情。

有时您还可以尝试帮助指导sql优化器。如果应该总是使用一个索引,那么可以将其作为查询提示给出。

我也很好奇知道,如果以下删除问题:如果所有的查询优化器修复被打开

select top 2000 Apps.object_id 
from 
(
    select Apps.object_id, Apps.mfrId 
    from Apps 
    where Apps.OBJECT_ID = 6879149 
) Apps 
left join Manufacturer ON (
     Apps.mfrId = Manufacturer.Id 
     OR Apps.mfrId IS NULL 
     OR Apps.mfrId = 0) 

,问题仍然存在,我们要问,为什么查询优化器做这个。它只能使用存在的索引,并且只有在存在统计信息的情况下才能确定使用索引是否有利。同时,过时的统计数据将导致不好的选择。定期重建/重组索引并更新统计信息可能会很好。你可能想尝试这样做,看看它是否有任何影响。

结论

既然你已经有了一个固定的工作,不要使用它。但是你的问题是为什么两个非常相似的事情会达到非常不同的结果。假设正在使用相同的参数,并且包括两个选项非常相似的事实,则问题指向查询优化器是一个不好的选择。这表明您可能需要安装最新的修补程序(如果未安装),并且还启用4199以实际启用您已通过sql server修补程序安装的所有查询优化程序修补程序。这包括将OPTION(QUERYTRACEON 4199)添加到sql的底部,或者在全局启用4199或类似的。

+0

想法是这样的。我们有一个应用程序(一个应用程序是在车辆上使用零件的地方)。如果应用程序有制造商设置,那么该车辆与该制造商相关联。如果车辆没有制造商设置(其mfrid为0或null),则它与所有制造商相关联。 –

+0

是的,但为什么ISNULL的变化要快几百倍,为什么当其他版本的查询得到5 - 30 1222错误时,为什么不会得到大量的错误1222。 –

+0

雅,我已经通过优化器运行它,并使用SQL管理工作室中的比较选项来比较执行计划。我得到了它展示给我的概念,但在这种情况下对我来说并不是非常有用。 –