sql - 为什么 SQL Server 标量值函数变得更慢?

标签 sql sql-server tsql sql-server-2005 sql-function

为什么标量值函数似乎会导致查询运行速度随着连续使用次数的增加而逐渐变慢?

我有一个使用从第 3 方购买的数据构建的表。

我删除了一些内容以使这篇文章变得更短......但只是为了让您了解事情是如何设置的。

CREATE TABLE [dbo].[GIS_Location](
        [ID] [int] IDENTITY(1,1) NOT NULL, --PK
        [Lat] [int] NOT NULL,
        [Lon] [int] NOT NULL,
        [Postal_Code] [varchar](7) NOT NULL,
        [State] [char](2) NOT NULL,
        [City] [varchar](30) NOT NULL,
        [Country] [char](3) NOT NULL,

CREATE TABLE [dbo].[Address_Location](
    [ID] [int] IDENTITY(1,1) NOT NULL, --PK
    [Address_Type_ID] [int] NULL,
    [Location] [varchar](100) NOT NULL,
    [State] [char](2) NOT NULL,
    [City] [varchar](30) NOT NULL,
    [Postal_Code] [varchar](10) NOT NULL,
    [Postal_Extension] [varchar](10) NULL,
    [Country_Code] [varchar](10) NULL,

然后我有两个函数来查找 LAT 和 LON。

CREATE FUNCTION [dbo].[usf_GIS_GET_LAT]
(
    @City VARCHAR(30),
    @State CHAR(2)
)
RETURNS INT 
WITH EXECUTE AS CALLER
AS
BEGIN
    DECLARE @LAT INT

    SET @LAT = (SELECT TOP 1 LAT FROM GIS_Location WITH(NOLOCK) WHERE [State] = @State AND [City] = @City)

RETURN @LAT
END


CREATE FUNCTION [dbo].[usf_GIS_GET_LON]
(
    @City VARCHAR(30),
    @State CHAR(2)
)
RETURNS INT 
WITH EXECUTE AS CALLER
AS
BEGIN
    DECLARE @LON INT

    SET @LON = (SELECT TOP 1 LON FROM GIS_Location WITH(NOLOCK) WHERE [State] = @State AND [City] = @City)

RETURN @LON
END

当我运行以下命令时...

SET STATISTICS TIME ON

SELECT
    dbo.usf_GIS_GET_LAT(City,[State]) AS Lat,
    dbo.usf_GIS_GET_LON(City,[State]) AS Lon
FROM
    Address_Location WITH(NOLOCK)
WHERE
    ID IN (SELECT TOP 100 ID FROM Address_Location WITH(NOLOCK) ORDER BY ID DESC)

SET STATISTICS TIME OFF

100 ~= 8 毫秒,200 ~= 32 毫秒,400 ~= 876 毫秒

--编辑 抱歉,我应该说得更清楚。我不想调整上面列出的查询。这只是一个示例,显示处理的记录越多,执行时间就越慢。在现实世界的应用程序中,这些函数用作 where 子句的一部分,以围绕城市和州构建半径,以包含该地区的所有记录。

最佳答案

在大多数情况下,最好避免引用表的标量值函数,因为(正如其他人所说)它们基本上是黑匣子,需要为每一行运行一次,并且不能由查询计划引擎优化。因此,即使关联表有索引,它们也倾向于线性扩展。

您可能需要考虑使用内联表值函数,因为它们是与查询一起内联计算的,并且可以进行优化。您获得了所需的封装,但获得了将表达式直接粘贴到 select 语句中的性能。

作为内联的副作用,它们不能包含任何过程代码(不声明 @variable; set @variable = ..; return)。但是,它们可以返回多行和多列。

您可以像这样重写您的函数:

create function usf_GIS_GET_LAT(
    @City varchar (30),
    @State char (2)
)
returns table
as return (
  select top 1 lat
  from GIS_Location with (nolock) 
  where [State] = @State
    and [City] = @City
);

GO

create function usf_GIS_GET_LON (
    @City varchar (30),
    @State char (2)
)
returns table
as return (
  select top 1 LON
  from GIS_Location with (nolock)
  where [State] = @State
    and [City] = @City
);

使用它们的语法也有点不同:

select
    Lat.Lat,
    Lon.Lon
from
    Address_Location with (nolock)
    cross apply dbo.usf_GIS_GET_LAT(City,[State]) AS Lat
    cross apply dbo.usf_GIS_GET_LON(City,[State]) AS Lon
WHERE
    ID IN (SELECT TOP 100 ID FROM Address_Location WITH(NOLOCK) ORDER BY ID DESC)

关于sql - 为什么 SQL Server 标量值函数变得更慢?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/800017/

相关文章:

sql-server - 通过XML解析SQL中的字符错误

SQL 按子查询分组

sql - 在不同条件下两次检索同一列

c# - 这种方法是从 C# 获取 SQL 值的好方法吗?

sql - 如何在 nvarchar 中正确插入换行符

sql - C#/SQL 2005 中读取 BLOB 数据的内存有效方法

sql-server - SQL Server - 根据另一列强制一列的唯一性

sql - 如何从SQL中选择给定时间间隔之间的数据

c# - 使用 ExecuteNonQuery() 时无法检测到 SQL 错误

mysql - CodeIgniter 查询数据库中的错误号 1064