質問

なぜスカラー値の関数は、クエリが連続して使用されるほど累積的に遅くなるのですか?

サードパーティから購入したデータで作成されたこのテーブルがあります。

この投稿を短くするためにいくつかの項目を削除しました...しかし、それであなたは物事がどのように設定されているかを知ることができます。

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を検索する2つの関数があります。

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句の一部として使用され、都市と州の周りに半径を構築し、その地域内のすべてのレコードを含めます。

役に立ちましたか?

解決

ほとんどの場合、テーブルを参照するスカラー値関数は避けることが最善です(他の人が言ったように)、それらは基本的にすべての行に対して1回実行する必要があり、クエリプランエンジンで最適化できないブラックボックスであるためです。したがって、関連付けられたテーブルにインデックスがある場合でも、直線的にスケーリングする傾向があります。

クエリでインラインで評価され、最適化できるため、インラインテーブル値関数の使用を検討することもできます。必要なカプセル化は得られますが、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プロファイラーを見て、CPU列とREADS列とDURATION列を確認してください。テストサイズを増やして、1秒、2秒、5秒よりも長いテストを含めます。

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.

結果セットの行に対してスカラー関数を実行している場合、グローバル最適化なしで行ごとにスカラー関数が実行されることに注意してください。

機能をインラインTVFでラップすることができます。これにより、はるかに高速になります。

http ://sqlblog.com/blogs/alexander_kuznetsov/archive/2008/05/23/reuse-your-code-with-cross-apply.aspx

結果セットのすべての行に対して、関数を2回呼び出します(DBへの2つの選択ヒット)。

クエリを高速化して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(何かを選択)は効率的なパターンではない可能性が高く、この場合は簡単に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 Server 2019では、 スカラーUDFインライン化

  

機能のインテリジェントなクエリ処理スイートの下の機能。 この機能により、SQL ServerでスカラーUDFを呼び出すクエリのパフォーマンスが向上します(SQL Server 2019プレビュー以降)

     

T-SQLスカラーユーザー定義関数

     

Transact-SQLで実装され、単一のデータ値を返すユーザー定義関数は、T-SQLスカラーユーザー定義関数と呼ばれます。 T-SQL UDFは、SQLクエリ全体でコードの再利用とモジュール性を実現するエレガントな方法です。一部の計算(複雑なビジネスルールなど)は、命令型UDF形式で表現する方が簡単です。 UDFは、複雑なSQLクエリの作成に関する専門知識を必要とせずに、複雑なロジックを構築するのに役立ちます。

     

スカラーUDFは通常、次の理由によりパフォーマンスが低下します。

     
      
  • 反復呼び出し
  •   
  • 原価計算の欠如
  •   
  • 解釈された実行
  •   
  • シリアル実行
  •   
     
     

スカラーUDFの自動インライン化

     

スカラーUDFインライン化機能の目標は、UDFの実行が主なボトルネックであるT-SQLスカラーUDFを呼び出すクエリのパフォーマンスを向上させることです。

     

この新しい機能により、スカラーUDFは、UDF演算子の代わりに呼び出し元クエリで置換されるスカラー式またはスカラーサブクエリに自動的に変換されます。これらの式とサブクエリは最適化されます。 その結果、クエリプランにはユーザー定義の関数演算子はなくなりますが、ビューやインラインTVFなど、プランでその効果が観察されます。

     
     

インライン化可能なスカラーUDFの要件

     

スカラーT-SQL UDFは、次のすべての条件が満たされる場合、インラインになります   true:

     
      
  • UDFは、次の構成要素を使用して記述されています。

         
        
    1. DECLARE、SET:変数の宣言と割り当て。
    2.   
    3. SELECT:単一または複数の変数割り当てを使用したSQLクエリ1。
    4.   
    5. IF / ELSE:ネストの任意のレベルでの分岐。
    6.   
    7. RETURN:単一または複数のreturnステートメント。
    8.   
    9. UDF:ネストされた/再帰的な関数呼び出し2。
    10.   
    11. その他:EXISTS、ISNULLなどの関係演算。
    12.   
  •   
  • UDFは、時間依存(GETDATE()など)または副作用がある組み込み関数3(   NEWSEQUENTIALID())。

  •   
  • UDFはEXECUTE AS CALLER句を使用します(EXECUTE AS句が指定されていない場合のデフォルトの動作)。
  •   
  • UDFはテーブル変数またはテーブル値パラメーターを参照しません。
  •   
  • スカラーUDFを呼び出すクエリは、GROUP BY句でスカラー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