3

在我过去的经验中,我总是在需要选择数据集的简单情况下使用函数,没有很多复杂的逻辑,而且我还需要传递一个参数。

我最近被告知我应该不惜一切代价避免在 MSSQL 中使用函数,因为它们经常会导致性能问题,有时它们的使用会导致索引无法正确使用。任何人都可以谈谈这一点,并进一步详细解释这是否属实,以及背后的一些原因吗?

4

2 回答 2

3

你被天真地劝告过。


标量函数

WHERE dbo.fn_get_year(tbl.field) = 2012将混淆tbl.field并使其上的任何索引都无法使用。

例如,您会发现使用WHERE tbl.field >= '20120101' AND tbl.field < '20130101'.

在第一个示例中,必须处理每条记录,因为优化器无法看穿函数并推断出符合条件的记录范围。

在第二个示例中,您非常清楚您想要从点 a 到点 b 的连续记录块。这使优化器能够使用索引进行范围查找。


表值函数

所有这些SELECT * FROM dbo.my_function(@parameter) AS data. 以这种方式使用表值函数没有任何问题。

将函数的结果连接到另一个表或函数时会变得复杂。

如果函数是多语句(带有IF块等),则在处理连接之前返回函数的整个结果集。

如果该函数是一个内联函数(只有一个RETURNS TABLE AS SELECT blah FROM blah,那么 SQL Server 会将其视为一个宏(除非您告诉它不要这样做)。这意味着您的函数代码被替换到您的查询中,并且为您的查询构建了一个全新的执行计划。这可能意味着由于索引优化等原因,只会处理您函数中的相关记录。


简而言之,请向建议您的人询问他们的建议特别具体。如果它仍然存在never use functions,请忽略它们。

于 2012-08-08T13:32:26.360 回答
1

IMO 的反做法是在查询的 WHERE 子句中使用标量函数,而不使用任何其他可以为 SQL 提供良好选择性的过滤器。

例如

   SELECT columns
   FROM [table]
   WHERE dbo.myFunc(col1) = 55

通常会导致表扫描,而不管col1.

正如其他人指出的那样,有一些例外,例如,可以在索引计算列中使用确定性的模式绑定函数。

例如,考虑以下确定性函数:

CREATE FUNCTION dbo.myFunc(@id int)
returns int
WITH SCHEMABINDING
AS
    BEGIN
        return (@id + 1)
    END

给定表(使用 MSSQL 默认 PK = 聚集索引)

CREATE TABLE MyTable
(
    ID INT Identity (1,1),
    SomeOtherColumn VARCHAR(50),

    CONSTRAINT PK_MyTable PRIMARY KEY(ID)
)

填充了约 10 万条记录

select * from MyTable where ID < 100 -- Index Seek :)

但是,运行标量函数并没有获得聚集索引的好处

select * from MyTable where dbo.MyFunc(Id) < 100 -- Index Scan :(

使用标量函数作为计算列的基础

alter table MyTable add Computed as dbo.MyFunc(ID)

select * from MyTable where Computed < 100 -- Still Index Scan :(

-- 但是,因为 Computed 列是确定性的并且是模式绑定的,所以它可以被索引:

CREATE INDEX IX1_MyTable on MyTable(Computed) 

select * from MyTable where Computed < 100 -- Index Seek :)

有趣的是,现在应用该函数会导致索引查找 (SQL 2008R2)

select * from MyTable where dbo.MyFunc(ID) < 100 -- Index Seek :)
于 2012-08-08T13:33:10.153 回答