这让我很好奇,所以我做了一些基本的测试。我创建了一个带有一些不同值和一些重复值的小表,一个仅执行字符串连接的函数,然后查看了以下执行计划:
Go
DBCC DROPCLEANBUFFERS
DBCC FREEPROCCACHE
select distinct cola, colb, dbo.sillyfunc(cola, colb)
from distincttest
--Clear the cache
Go
DBCC DROPCLEANBUFFERS
DBCC FREEPROCCACHE
select cola, colb, dbo.sillyfunc(cola, colb)
from (select distinct cola, colb from distincttest) as t
在这种情况下,执行计划清楚地表明,第一个为每一行运行连接函数,但第二个首先对不同的值进行排序,然后运行该函数。但是对于少数行,它们具有相同的执行时间,当一起运行时,它们显示每行使用了总查询资源的 50%。
所以,我添加了几十万个重复行。并再次尝试。这改变了查询计划,因此它正在执行哈希匹配以获得独特性,而不是以前的排序,现在第二个版本迫使它首先选择独特性,执行速度快了十倍以上。
最后,我认为这可能只是因为 SQL Server 将我的 sillyfunc 标记为非确定性(select OBJECTPROPERTYEX(object_id('dbo.sillyfunc'), 'isdeterministic')
返回 0),所以我切换到patindex,它是一个内置函数,被认为是确定性的。这给了我相同的结果,第一个版本中的每一行都调用了该函数,而第二个版本中只有少数不同的行调用了该函数。
因此,进一步的测试可能会发现诱使优化器做一些更复杂的事情的情况,但看起来如果你想在调用函数之前应用 distinct ,那么你需要使用子查询、CTE 或临时表来限制函数可以访问的内容。