我创建了两个表,我们称之为foo
和bar
。两个表都包含一个数字。 foo
在此列上有一个索引。 foo
包含大量行,而bar
只有少数几行。然后我写了
select *
from foo
where number in (select number from bar)
正如我所料,此查询执行速度非常快。它找到了我在bar中的3条记录,即使foo有80,000条记录,它也会对这三个值进行索引查找,并在几分之一秒内返回结果。
然后我编写了一个函数“echo”,它除了取一个整数作为参数并返回该整数外,什么都不做,即echo的整个文本是“return @x”。
然后我尝试这个查询:
select *
from foo
where number in (select dbo.echo(number) from bar)
使用完全相同的数据,此查询要慢得多。当我检查查询计划时,结果是第一个查询使用索引快速查找所选记录,但第二个查询没有,而是对整个表进行顺序搜索。
请注意,这与我们渲染索引无效的查询不同。就像我说的那样,“dbo.echo(number)= 42”,那么确定,数字上的索引现在没用了(可能是数据库引擎不知道函数只返回它的输入,就像这样的函数在现实生活中毫无用处,我并不感到惊讶他们没有特别处理它,所以数据库引擎别无选择,只能按顺序搜索整个表格。但是在这里,它在bar上的字段上执行函数。然后它应该能够在foo的索引中查找该值,就像我给出一个未包含在函数中的值一样。
为什么呢?更重要的是,有什么方法可以解决这个问题吗?当然导致这个实验的真正问题是一个功能,它不仅仅返回它的参数,它实际上正在做一些解密。但我在这里的实验证明,仅使用函数会削弱查询计划。
这是我实验的实际表格:
CREATE TABLE [dbo].[foo]
(
[id] [int] IDENTITY(1,1) NOT NULL,
[name] [varchar](50) NULL,
[number] [int] NULL,
PRIMARY KEY CLUSTERED ([id] ASC)
)
CREATE INDEX [ix_foo_number] ON [dbo].[foo] ([number] ASC)
CREATE TABLE [dbo].[bar]
(
[id] [int] IDENTITY(1,1) NOT NULL,
[number] [int] NULL,
[some_value] [varchar](10) NULL,
PRIMARY KEY CLUSTERED ([id] ASC)
)
create function [dbo].[echo](@x int) returns int
as
begin
return @x
end
然后我用82,000条记录填充了foo,并且记录了3条记录。
查询是:
select * from foo where number in (select number from bar)
select * from foo where number in (select dbo.echo(number) from bar)
第一个查询的计划成本为0.025,第二个查询的计划成本为0.848。成本超过30倍,因为我添加了无操作功能。好吧,仍然很小,但在提示这一点的真实查询中,成本飙升至超过500并导致超时。
答案 0 :(得分:1)
我可以向您推荐这篇讨论SQL Server中标量函数性能问题的优秀博文:
T-SQL User-Defined Functions: the good, the bad, and the ugly (part 1)
请注意,还有其他部分更详细。
从上面的评论中添加:
优化器基本上不做任何事情。因此,尽管有三个值,但它并不知道。它会为了比较它所需要的每条记录调用该函数,所以在你的情况下是82,000次。它没有"知道"结果是确定性的。它没有"知道"结果不会随着每次执行而改变(例如,GETDATE()确实如此)。因此,它不能也不能缓存"它
您可以尝试定义功能" WITH SCHEMABINDING"看看是否有任何影响...我理解它在某些情况下确实如此,但它还没有真正帮助我。我不完全理解这里的细微差别,因为我已经看到使用函数的地方要快得多,但在大多数情况下,它的速度要慢得多。
答案 1 :(得分:0)
在第一个选择中使用clear值作为条件,但在第二个查询中,服务器必须执行3次函数,然后使用函数的结果作为条件。
答案 2 :(得分:0)
这很慢,因为你的事情倒退了。当您搜索解密的值时,引擎别无选择,只能解密表中每行的值以进行比较。有关索引的类似讨论,请参阅Indexing an encrypted column in sql server - 这可能是您需要解决的下一步问题。如果你搜索“搜索加密列”这几个字,你会进行更多的讨论 - 如果加密是一个主要问题,一些非常重要的讨论。