为什么标值的功能似乎会引起查询要运行累积速度较慢的更多的时间在继承,他们使用?

我有这个表格,是建立与购买数据从一个第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,

然后,我有两个功能,看起来纬度和经度。

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-=32ms,400~=876ms

-编辑 对不起我应该已经更加清楚。我不想为你的查询,上面列出的。这只是个样本显示的执行时间越来越慢越的记录,它的危机的通过。在现实世界的应用程序的功能的一部分用作其中一个条款,以建立一个周围半径的一个城市和国家,包括所有记录与在该区域。

有帮助吗?

解决方案

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

您可能需要考虑使用内联表值函数,因为它们是与查询内联求值的,并且可以进行优化。您可以获得所需的封装,但是可以在select语句中粘贴表达式。

作为内联的副作用,它们不能包含任何过程代码(没有声明@variable;设置@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事件探查器,查看CPU和READS和DURATION列。增加测试大小以包括花费超过一秒,两秒,五秒的测试。

CREATE FUNCTION dbo.slow
(
    @ignore int
)
RETURNS INT 
AS
BEGIN
    DECLARE @slow INT
    SET @slow = (select count(*) from sysobjects a 
        cross join sysobjects b 
        cross join sysobjects c 
        cross join sysobjects d 
        cross join sysobjects e 
        cross join sysobjects f
    where a.id = @ignore) 

    RETURN @slow
END
go
SET STATISTICS TIME ON

select top 1 dbo.slow(id)
from sysobjects
go
select top 5 dbo.slow(id)
from sysobjects
go
select top 10 dbo.slow(id)
from sysobjects
go
select top 20 dbo.slow(id)
from sysobjects
go
select top 40 dbo.slow(id)
from sysobjects

SET STATISTICS TIME OFF

输出

SQL Server Execution Times:
   CPU time = 203 ms,  elapsed time = 202 ms.


SQL Server Execution Times:
   CPU time = 889 ms,  elapsed time = 939 ms.

SQL Server Execution Times:
   CPU time = 1748 ms,  elapsed time = 1855 ms.

SQL Server Execution Times:
   CPU time = 3541 ms,  elapsed time = 3696 ms.


SQL Server Execution Times:
   CPU time = 7207 ms,  elapsed time = 7392 ms.

请记住,如果对结果集中的行运行标量函数,则标量函数将按行执行,不进行全局优化。

对结果集中的每一行调用该函数两次(对数据库选择两次)。

使您的查询更快地加入GIS_Location并跳过函数:

SELECT
    g.Lat,
    g.Lon
FROM
    Address_Location        l WITH(NOLOCK)
    INNER JOIN GIS_Location g WITH(NOLOCK) WHERE l.State = g.State AND l.City = g.City
WHERE
    ID IN (SELECT TOP 100 ID FROM Address_Location WITH(NOLOCK) ORDER BY ID DESC)

我不知道为什么NOLOCK,或者疯狂的where子句,我只是从问题中复制过来......

简单地说,因为具有用户定义函数的SQL表达式效率低于没有它们的SQL表达式。执行逻辑无法优化;并且必须为每一行产生函数开销(包括调用协议)。

KMike的建议很好。 WHERE .. IN(SELECT something)不太可能是一个有效的模式,在这种情况下可以很容易地用JOIN替换。

看看这是否更好......或者可能是一个独特的内部联接?

select a.*,
(select top 1 g.Lat from GIS_Location g where g.City = a.City and g.State = a.State) as Lat,
(select top 1 g.Lon from GIS_Location g where g.City = a.City and g.State = a.State) as Lon
from Address_Location a
where a.ID in (select top 100 ID from Address_Location order by ID desc)

至于标量函数的性能,我不确定。

通常标量功能远远慢于内联TVF同行。幸运的是,对于许多情况下,它将会改变。

SQL服务器2019年将介绍 标UDF内联:

一个设在智能查询的处理套件的功能。 这种功能可以提高性能的查询,调用的标Udf在SQL服务器(开始SQL服务器2019年的预览)

T-SQL标用户定义功能

用户限定的职能,执行办-SQL和返回的一个单一的数据的价值被称为T-SQL标用户定义的功能。 T-SQL Udf是一个优雅的方式来实现码的再利用和模块化跨SQL查询。一些计算(例如复杂的业务的规则)更易于表达必须UDF形式。Udf帮助建立复杂的逻辑,而不需要专门知识编写复杂的SQL查询。

标Udf通常结束了执行不佳,由于以下原因。

  • 调用的迭代
  • 缺乏成本计算
  • 解释执行
  • 序的执行

自动内联的标Udf

我们的目标的标UDF内联要素是要提高性能的查询,援引T-SQL标Udf,其中UDF执行的主要瓶颈。

这个新的特征,标Udf是自动转换为标量表达或标查询是取代在调查询地位操作人员。这些表达方式和子查询后进行优化。 结果,查询计划将不再具有用户自定义功能的操作者,但其影响会观察到,在该计划中,如意见或内联Tvf.


Inlineable标Udf要求

标T-SQL UDF可行内如果以下所有条件 是真实的:

  • UDF写的是使用以下结构:

    1. 声明,设置:变量的声明和任务。
    2. 选择:SQL query与单个/多个变量assignments1.
    3. 如果/ELSE:支与任意水平的嵌套。
    4. 返回:单个或多个返回的发言。
    5. UDF:嵌/递归功能calls2.
    6. 其他:关系操作如存在,函数.
  • UDF没有援引任何特性的功能,要么依赖于时间(例如GETDATE())或具有侧effects3(例如 NEWSEQUENTIALID()).

  • UDF使用的行为叫条款(默认行为,如果执行条款没有规定)。
  • UDF不准表的变量或表值的参数。
  • 查询调用一个标UDF不会参照一个标UDF呼吁在其组通过的条款。
  • UDF不是本地汇编的(互操作是支持)。
  • UDF不是使用在计算列或一个检查制约的定义。
  • UDF不参考用户定义的类型。
  • 有没有签名添加到位。
  • UDF不是分隔的功能。

检查功能是可内联:

SELECT OBJECT_NAME([object_id]) AS name, is_inlineable
FROM sys.sql_modules
WHERE [object_id] = OBJECT_ID('schema.function_name')

启用/停用功能在数据库的水平:

ALTER DATABASE SCOPED CONFIGURATION SET TSQL_SCALAR_UDF_INLINING = ON/OFF;
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top