我已经对我们正在处理的项目进行了SQL性能审核,并且出现了一个“关键”项目:
这种通配符查询模式将导致表扫描,从而导致 查询性能不佳。
SELECT *
FROM TabFoo
WHERE ColBar = @someparam OR @someparam IS NULL
他们的建议是:
在许多情况下,OPTION(RECOMPILE)提示可以是一种快速解决方法。 从设计的角度来看,您也可以考虑使用单独的If 子句或(不推荐)使用动态SQL语句。
动态SQL肯定不是正确的前进方式。基本上,程序是我搜索某些东西或其他东西的程序。两个参数进入程序,我正在过滤一个或另一个。
比他们展示的更好的例子是:
SELECT ..
FROM...
WHERE (ColA = @ParA OR @ColA IS NULL)
(AND ColB = @ParB OR @ParB IS NULL)
这是不好的做法,除了动态SQL(因为,我认为动态SQL无法真正编译并且在执行计划中更有效率?),如何做到最好?
答案 0 :(得分:2)
像
这样的查询select *
from foo
where foo.bar = @p OR @p is null
可能会也可能不会导致表扫描。我的经验是它不会:假设存在合适的索引,优化器完全能够在表达式foo.bar = @p
上进行索引搜索。此外,如果变量为空,它完全能够短路。在尝试并检查绑定的执行平面之前,您不会知道您的执行计划是什么样的。然而,更好的技术是:
select *
from foo
where foo.bar = coalesce(@p,foo.bar)
会给你相同的行为。
如果您正在使用存储过程,那么可以并且会在takeus中咬你的东西是这样的:
create dbo.spFoo
@p varchar(32)
as
select *
from dbo.foo
where foo.bar = @p or @p = null
return @@rowcount
在where子句中直接使用存储过程参数将导致缓存的执行计划在其首次执行时基于@p
的值。这意味着,如果存储过程的第一次执行具有@p
的异常值,则可能会获得一个缓存的执行计划,该计划对于95%的“正常”执行执行效果非常差,并且仅适用于奇怪的情况。为防止这种情况发生,您希望这样做:
create dbo.spFoo
@p varchar(32)
as
declare @pMine varchar(32)
set @pMine = @p
select *
from dbo.foo
where foo.bar = @pMine or @pMine = null
return @@rowcount
将参数简单分配给局部变量使其成为表达式,因此缓存的执行计划不会绑定到@p
的初始值。不要问我怎么知道这个。
您收到的建议:
在许多情况下,OPTION(RECOMPILE)提示可以是一种快速解决方法。 从设计的角度来看,您也可以考虑单独使用 如果子句或(不推荐)使用动态SQL语句。
是h .. Option(recompile)
表示每次执行时都会重新编译存储过程。在编译存储过程时,编译时锁定取决于依赖对象。此外,在编译完成之前,没有其他人能够执行存储过程。我们应该说,这对并发性和性能有负面影响。使用option(recompile)
应该是最后的手段。
编写干净的SQL并使用生产数据审核您的执行计划,或尽可能接近它:您获得的执行计划受数据的大小和形状/分布的影响。
答案 1 :(得分:1)
我可能错了,但我很确定无论你的where
子句中的列没有编入索引,都会发生表扫描。此外,您可以通过重新排序OR
子句来获得更好的性能,这样如果@ParA IS NULL
为真,则首先进行评估,而不需要评估列中的值。需要记住的是,where
子句是针对从from
子句返回的每一行进行评估的。我不建议使用动态SQL,老实说,即使在相对较重的负载下,我也很难相信这种形式的过滤器会导致显着的性能损失,因为只要列未被索引,就需要进行表扫描。 / p>
答案 2 :(得分:1)
我们进行了一次Microsoft订婚,他们注意到我们有很多这种“通配符模式用法”,他们的建议是将查询转换为IF / ELSE结构...
# make a new column with Unix time as @ForceBru mentioned
start_date = '1970-01-01'
df3['helper'] = pd.to_datetime(start_date)
# convert your column of JSON dates / numbers to days
df3['timestamp_ms'] = df3['timestamp_ms'].apply(lambda x: (((x/1000)/60)/60/24))
# add a day adder column
df3['time_added'] = pd.to_timedelta(df3['timestamp_ms'],'d')
# add the two columns together
df3['actual_time'] = df3['helper'] + df3['time_added']
他们更喜欢这种方法,而不是重新编译(增加执行时间)或动态代码(无法提前计划,同样的事情,每次都必须弄清楚计划);而且我似乎还记得,即使使用局部变量,它仍然是一个问题(此外,无论如何都需要额外的内存)。
如果编写包含多个WPU问题的查询,您会发现事情有些疯狂,但至少对于较小的问题,MS建议使用IF / ELSE方法。
在我看到的所有示例中,都涉及到NULL,但是我不禁要想,如果您有一个使用默认值的参数,无论是参数本身还是使用ISNULL()进行设置,并且使用的模式基本上相同,这也可能是不好的(好吧,只要默认值是“实际值”就永远不会是)。