似乎SQL Server无法按功能结果进行搜索。有什么方法吗?

时间:2017-05-03 17:48:37

标签: sql-server database performance optimization

我创建了两个表,我们称之为foobar。两个表都包含一个数字。 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并导致超时。

3 个答案:

答案 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 - 这可能是您需要解决的下一步问题。如果你搜索“搜索加密列”这几个字,你会进行更多的讨论 - 如果加密是一个主要问题,一些非常重要的讨论。