17

スカラー値関数を連続して使用すると、クエリの実行が累積的に遅くなるように見えるのはなぜですか?

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

この投稿を短くするために、いくつかの項目を削除しました... しかし、設定方法を理解していただくためです。

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

4

8 に答える 8

31

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

クエリでインラインで評価され、最適化できるため、インライン テーブル値関数の使用を検討することをお勧めします。必要なカプセル化が得られますが、選択ステートメントに式を直接貼り付けるパフォーマンスが得られます。

インライン化の副作用として、手続き型コードを含めることはできません (@variable を宣言しない; @変数 = ..; を返す)。ただし、複数の行と列を返すことができます。

関数を次のように書き直すことができます。

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)
于 2009-05-01T00:53:29.920 に答える
3

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

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

于 2009-05-19T15:26:31.407 に答える
2

結果セットのすべての行に対して、関数を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句が、質問からコピーしたのかわかりません...

于 2009-04-28T22:11:28.987 に答える
0

簡単に言えば、ユーザー定義関数を含む SQL 式は、関数を含まない SQL 式よりも効率が悪いためです。実行ロジックを最適化できません。関数のオーバーヘッド (呼び出しプロトコルを含む) は、行ごとに発生する必要があります。

KMike のアドバイスは良いです。WHERE .. IN (何かを選択) は効率的なパターンではない可能性が高く、この場合は簡単に JOIN に置き換えることができます。

于 2009-04-28T22:18:56.283 に答える
0

これがうまく機能するかどうかを確認してください...または、別個の内部結合でしょうか?

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)

スカラー関数のパフォーマンスについては、よくわかりません。

于 2009-04-28T22:25:30.887 に答える
0

通常、スカラー関数は、対応するインライン 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 をインラインにすることができます。

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

    1. DECLARE、SET: 変数の宣言と代入。
    2. SELECT: 単一/複数の変数の割り当てによる SQL クエリ1。
    3. IF/ELSE: 任意のレベルのネストによる分岐。
    4. RETURN: 単一または複数の return ステートメント。
    5. UDF: ネストされた/再帰的な関数呼び出し 2.
    6. その他: EXISTS、ISNULL などの関係演算。
  • 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;

補遺

マイクロソフト リサーチ - Project Froid

于 2018-11-07T15:36:08.317 に答える