Articles

Creating and Using Inline Table-Valued Functions

Posted on

忘れてしまったり、これまでに出会ったことがない場合に備えて、インラインテーブル値関数とは何かを簡単に説明することから始めましょう。

Books Onlineのユーザー定義関数(UDF)の定義を参考にすると、インラインテーブル値関数(iTVF)とは、パラメータを受け取り、アクションを実行し、戻り値としてテーブルを提供できるテーブル式のことです。 iTVF の定義は、ビューと同様に、データベース オブジェクトとして永続的に保存されます。

Books Online (BOL) から iTVF を作成するための構文は、以下のとおりです。

CREATE FUNCTION function_name ( parameter_data_type } ] ) RETURNS TABLE ] RETURN select_stmt 

以下のコード スニペットは、iTVF の例です。

DEFAULT VALUE () – パラメータにデフォルト値を設定することができ、この例では @CommentDate のデフォルト値は 01 Jan 2006 です。

RETURNS TABLE – 関数の定義に基づいて仮想テーブルを返します。

SCHEMABINDING – 関数が参照するデータベース オブジェクトにバインドされることを指定します。 スキーマバインディングが指定されている場合、関数定義に影響を与えるような方法でベースオブジェクトを変更することはできません。

例のiTVFのSELECT文を見てみると、WHERE句に渡されたパラメータを除いて、ビューに配置するクエリに似ています。

iTVF は、定義がデータベースに永続的に保存されるという点ではビューに似ていますが、パラメータを渡すことができるということは、ロジックをカプセル化して再利用する方法があるだけでなく、渡したい特定の値を問い合わせることができるという柔軟性があります。

なぜ iTVF を使用するのか

iTVF の使用方法を説明する前に、なぜ iTVF を使用するのかを考えることが重要です。

  1. モジュール式の開発
  2. 柔軟性
  3. パフォーマンス ペナルティの回避

以下、それぞれについて少しずつ説明します。

モジュール式の開発が、保守可能なコードを犠牲にしてまで行われている場合を除いて、iTVF によるコードの再利用とカプセル化の利点は、私たちが最初に気付くことの 1 つです。

DECLARE @CommentDate DATE= '2008-01-01T00:00:00.000';SELECT u.Id, u.Reputation, MAX(c.CreationDate) AS LatestCommentDate, COUNT(*) AS TotalCommentsFROM dbo.Users u INNER JOIN dbo.Comments c ON u.Id = c.UserIdWHERE c.CreationDate >= @CommentDateGROUP BY u.Id, u.Reputation;GO

VS

DECLARE @CommentDate DATE= '2008-01-01T00:00:00.000';SELECT u.Id, u.Reputation, c.LatestCommentDate, c.TotalCommentsFROM dbo.Users u CROSS APPLY .(u.Id, @CommentDate) c;GO

iTVF を使用したクエリは単純で、より複雑なクエリで使用すると、根本的な複雑さを隠すことができ、開発努力を大いに助けることができます。 エンハンスメントやデバッグのために関数のロジックを微調整する必要があるシナリオでは、1 つのオブジェクトに変更を加えてテストすれば、その 1 つの変更が関数が呼び出される他の場所に反映されます。

柔軟性

柔軟性に関しては、パラメータ値を渡す関数の機能を使用して、簡単に対話できるようにすることです。

例えば、ロジックがストアド プロシージャにある場合、結果を一時テーブルに取り込み、データを操作するために一時テーブルを照会する必要があるでしょう。

ロジックがビューにある場合、パラメーターを渡すことはできません。 選択肢としては、ビューを照会し、ビューの定義の外で WHERE 句を追加することができます。

Avoiding Performance Penalties

この記事の範囲では、Common Language Runtime (CLR) UDF については見ません。

iTVF と他の UDF との間に違いが見られるのは、パフォーマンスです。 パフォーマンスについては多くのことが語られていますが、ほとんどの場合、他の UDF はパフォーマンスの面で非常に苦しんでいます。 iTFVではパフォーマンスが少し異なるため、代わりにiTVFに書き換えるという方法もあります。 iTVF は、スカラー関数や MSTVF (Multi Statement Table-Valued Functions) に影響するようなパフォーマンス上のペナルティを受けません。

iTVF は、その名前が示すように、実行プランにインライン化されます。

多くの人にとって、パフォーマンスだけが、スカラーまたは MSTVF の代わりに iTVF を使用する大きな理由となっています。 今はまだパフォーマンスを気にしていなくても、ボリュームが大きくなったり、ロジックが複雑に適用されたりすると、状況は急速に変化する可能性があるため、他のタイプの UDF の落とし穴を理解することは重要です。

さて、iTVF を使用する理由をいくつか挙げましたので、どのように使用するかを見ていきましょう。

セットアップ iTVF デモ

このデモでは StackOverflow2010 データベースを使用します。このデータベースは StackOverflow の素敵な方々から https://archive.org/details/stackexchange

代わりに、Brent Ozar Unlimited の素敵な方々からこちらのデータベースを入手できます。 https://www.brentozar.com/archive/2015/10/how-to-download-the-stack-overflow-database-via-bittorrent/

私は、隔離された環境で、ローカルの SQL Server 2016 Developer Edition 上に StackOverflow データベースを復元しました。 また、互換性モードを 2016 (130) に設定し、Intel i7 2.11 GHz の CPU と 16GB の RAM を搭載したマシンでデモを実行しています。

StackOverflow データベースを起動したら、この記事に含まれる 00_iTFV_Setup.sql リソース ファイル内の T-SQL を実行して、これから使用する iTVF を作成します。

iTVF を仮想テーブルとして使用する

iTVF はデータベースのどこにも保存されないことを覚えておいてください。 永続化されるのは、その定義です。

以下の 6 つの例では、iTVF を仮想テーブルとして扱う方法を紹介します。 StackOverflowのデータベースが起動しているマシンで、セクション1にある以下の6つのクエリを実行して、実際に確かめてみてください。 これらは、01_Demo_SSC_iTVF_Creating_Using.sqlというファイルにもあります。

/**************************************************************************1. Using iTVFs in queries as if it were a table.**************************************************************************/DECLARE @UserId INT= 3, @CommentDate DATE= '2006-01-01T00:00:00.000';--1.1 Get specific columnsSELECT UserId, LatestCommentDateFROM dbo.itvfnGetRecentComment(3, '2006-01-01T00:00:00.000');--1.2 Get all columns SELECT *FROM dbo.itvfnGetRecentComment(@UserId, @CommentDate);--1.3 Use a default valueSELECT UserId, LatestCommentDate, TotalCommentsFROM dbo.itvfnGetRecentComment(@UserId, DEFAULT);--1.4 In the WHERE clauseSELECT u.DisplayName, u.Location, u.ReputationFROM dbo.Users uWHERE u.Reputation <=( SELECT TotalComments FROM dbo.itvfnGetRecentComment(@UserId, DEFAULT));--1.5 In the HAVING clauseSELECT u.Location, SUM(u.Reputation) AS TotalRepFROM dbo.Users uGROUP BY u.LocationHAVING SUM(u.Reputation) >=( SELECT rc.TotalComments FROM dbo.itvfnGetRecentComment(3, DEFAULT) rc);--1.6 In the SELECT clauseSELECT u.Location, CASE WHEN SUM(u.Reputation) >=( SELECT TotalComments FROM dbo.itvfnGetRecentComment(3, DEFAULT)) THEN 'YES' ELSE 'NO' END AS InRepLimitFROM dbo.Users uGROUP BY u.Location;

上記のように、iTVF と対話すると、以下のことができます。

  1. 完全なカラムリストを返す
  2. 指定したカラムを返す
  3. キーワードを渡してデフォルト値を使用する。 DEFAULT
  4. WHERE 句に iTVF を渡す
  5. HAVING 句に iTVF を使用する
  6. SELECT 句に iTVF を使用する

これを整理してみましょう。 通常、ストアド プロシージャでは、結果セットを一時テーブルに挿入し、一時テーブルを操作して上記の処理を行います。

入れ子になった関数の中のiTVF

関数の入れ子は、下手をするとそれなりの問題を引き起こしますが、iTVFの中ではそれが可能です。

今回の例では、dbo.itvfnGetRecentCommentByRep という関数が、ユーザーの最新のコメントや総コメント数などを返します。

SELECT DisplayName, LatestCommentDate, ReputationFROM .(3, DEFAULT, DEFAULT);GO

最後のコメントの日付を返す場合、これは別の関数である dbo.itvfnGetRecentComment の呼び出しを介して行われます。

CREATE OR ALTER FUNCTION . (@UserId int, @Reputation int = 100, @CommentDate datetime = '2008-01-01T00:00:00.000')RETURNS TABLE WITH SCHEMABINDING AS RETURN ( SELECT u.DisplayName, u.Reputation, c.LatestCommentDate, c.TotalCommentsFROM dbo.Users u OUTER APPLY .(u.Id, @CommentDate) cWHERE u.Id = @UserId AND u.Reputation >= @Reputation);GO

この例では、関数を入れ子にしても、基本的な定義はインライン化され、対話するオブジェクトはクラスター化されたインデックスである dbo.Users と dbo.Comments であることがわかります。

計画では、述語をサポートする適切なインデックスがないため、Commentsテーブルのクラスター化されたインデックススキャンが実行され、並行して実行回数を8回にしています。 UsersテーブルからUserId 3をフィルタリングするので、Usersテーブルをシークし、GROUP BY(ストリーム集約)後にCommentsテーブルに結合して最新のコメントの日付を取得します。

iTVF のネストされた関数

APPLY 演算子の使用

APPLY 演算子には非常に多くの創造的な使い方があり、iTVF との対話に適用できるオプションであることは間違いありません。 APPLY演算子の詳細については、以下のドキュメントを参照してください。 https://docs.microsoft.com/en-us/previous-versions/sql/sql-server-2008-r2/ms175156(v=sql.105) および http://www.sqlservercentral.com/articles/Stairway+Series/121318/

以下の例では、すべてのユーザーの iTVF を呼び出して、2008 年 1 月 1 日以降の LatestCommentDate、Total Comments、Id、および Reputation を返したいとします。 これには APPLY 演算子を使用し、Users テーブルの Id と CommentDate を渡して、Users テーブル全体の各行に対して実行します。

DECLARE @CommentDate DATE= '2008-01-01T00:00:00.000';SELECT u.Id, u.Reputation, c.LatestCommentDate, c.TotalCommentsFROM dbo.Users u OUTER APPLY .(u.Id, @CommentDate) c;GO

JOINにおけるiTVF

2つ以上のテーブルを結合するのと同様に、iTVFを他のテーブルとの結合に使用することができます。 ここでは、2008年1月1日以降に「Student」のバッジを取得したユーザーについて、UsersテーブルからDisplayNameを、iTVFからUserId、BadgeName、BadgeDateを取得します。

DECLARE @BadgeName varchar(40)= 'Student', @BadgeDate datetime= '2008-01-01T00:00:00.000';SELECT u.DisplayName, b.UserId, b.BadgeName, b.BadgeDateFROM dbo.Users u INNER JOIN .(@BadgeName, @BadgeDate) b ON b.UserId = u.Id;GO

他の UDF の代替

前述の記事では、iTVF を使用する理由として、スカラーや MSTVF などの他のタイプの UDF が被るパフォーマンス上のペナルティを回避するためであると述べました。 このセクションでは、スカラーおよび MSTVF を iTVF に置き換える方法を示し、次のパートでは、なぜそうすることを選択するのかを見ていきます。

スカラー UDF の代替

先に進む前に、次のクエリを実行して、まもなく使用するスカラー UDF、dbo.sfnGetRecentComment を作成します。

USE GOCREATE FUNCTION . (@UserId int, @CommentDate date = '2006-01-01T00:00:00.000')RETURNS datetimeWITH SCHEMABINDING, RETURNS NULL ON NULL INPUTASBEGIN DECLARE @LatestCreationDate datetime; SELECT @LatestCreationDate = MAX(CreationDate) FROM dbo.Comments WHERE UserId = @UserId AND CreationDate >= @CommentDate GROUP BY UserId; RETURN @LatestCreationDate;END;GO

スカラー関数を作成した後、次のクエリを実行します。 ここでは、スカラー関数にカプセル化されたロジックを使用して、Users テーブル全体を照会し、2008 年 1 月 1 日以降の各ユーザーの ID と最新のコメントの日付を返したいと思います。 これを行うには、SELECT 句にこの関数を追加し、パラメータ値として Users テーブルの Id と先ほど設定したコメントの日付 (2008 年 1 月 1 日) を渡します。

DECLARE @CommentDate DATE= '2008-01-01T00:00:00.000';SELECT u.Id, .(u.Id, @CommentDate) AS LatestCommentDateFROM dbo.Users u;

同様のクエリを iTVF を使用して記述することができ、次のようなクエリになります。 ここでは、iTVF を使用して 2008 年 1 月 1 日以降の ID と最新のコメントの日付を取得したいので、Users テーブルのすべての行に対して APPLY 演算子を使用することで実現しています。

DECLARE @CommentDate DATE= '2008-01-01T00:00:00.000';SELECT u.Id, c.LatestCommentDateFROM dbo.Users u OUTER APPLY .(u.Id, @CommentDate) c;GO

MSTVF の代替

次のクエリを実行して、これから使用する MSTVF dbo.mstvfnGetRecentComment を作成します。

USE GOCREATE FUNCTION . (@UserId int, @CommentDate datetime = '2008-01-01T00:00:00.000')RETURNS @RecentComment TABLE( int NULL, date NULL, int NULL)WITH SCHEMABINDING ASBEGIN INSERT INTO @RecentComment SELECT c.UserId, MAX(c.CreationDate) AS LatestCommentDate, COUNT(*) AS TotalComments FROM dbo.Comments c WHERE c.UserId = @UserId AND c.CreationDate >= @CommentDate GROUP BY c.UserId; RETURN;END;GO

MSTVF を作成したので、これを使用して、2008 年 1 月 1 日以降の Users テーブルのすべての行の最新コメントの日付を返します。 前述の APPLY 演算子を使用した iTVF の例と同様に、APPLY 演算子を使用して Users テーブルのすべての行に関数を「適用」し、各行のユーザー ID と設定したコメントの日付を渡します。

DECLARE @CommentDate date= '2008-01-01T00:00:00.000';SELECT u.Id, c.LatestCommentDateFROM .Users u OUTER APPLY .(u.Id, @CommentDate) c;GO

例の MSTVF を iTVF に置き換えると、呼び出される関数の名前を除いて、結果は非常によく似ています。 今回の例では、前の例の MSTVF の代わりに、vfnGetRecentComment を呼び出し、SELECT 句の列を保持します。 これにより、Users テーブルのすべての行について、iTVF を使って 2008 年 1 月 1 日以降の ID と最新のコメントの日付を取得することができます。

DECLARE @CommentDate DATE= '2008-01-01T00:00:00.000';SELECT u.Id, c.LatestCommentDateFROM dbo.Users u OUTER APPLY .(u.Id, @CommentDate) c;GO

A Preference for iTVFs

スカラー関数や MSTVF よりも iTVF を選択する理由は、パフォーマンスです。

ここでは、前のセクションで実行したクエリの例に基づいて 3 つの関数のパフォーマンスを簡単に把握し、関数の実行時間や実行回数などのメトリクス全体でパフォーマンスを比較する以外は、あまり詳しく説明しません。 このデモで使用するパフォーマンス統計のコードは、Grant Fritchey 氏による SQL Server Execution Plans の第 3 版のコード サンプルにあるリスト 9.3 を使用しています。

DECLARE @CommentDate DATE= '2008-01-01T00:00:00.000';SELECT u.Id, .(u.Id, @CommentDate) AS LatestCommentDateFROM dbo.Users u;

スカラー関数は並列性を阻害し、スカラー関数を持つステートメントは各行に対して 1 回ずつ呼び出されることがよく知られています。 テーブルの行数が10行であれば問題ないかもしれません。 数千行になると、パフォーマンスが大幅に低下することに驚くかもしれません(あるいは驚かないかもしれません)。

まず最初に行いたいことは、SQL Server のキャッシュと再利用可能な既存のプランを (隔離された環境で) クリアし (FREEPROCCACHE)、最初にディスクからデータを取得し (DROPPCLEANBUFFERS)、コールド キャッシュを開始することです。

この後、Users テーブルに対して SELECT クエリを実行し、スカラー関数を使用して ID と LatestCommentDate を返します。 パラメータ値として、UsersテーブルのIdと、先ほど2008年1月1日に設定したコメントの日付を渡します。

-- run in an isolated environmentCHECKPOINT;DBCC DROPCLEANBUFFERS;DBCC FREEPROCCACHE;GODECLARE @CommentDate DATE= '2008-01-01T00:00:00.000';SELECT u.Id, .(u.Id, @CommentDate) AS LatestCommentDateFROM dbo.Users u;

スカラー関数を使用した SELECT クエリは単純な構造に見えるかもしれませんが、私のマシンでは 4 分後にまだ実行されていました。

反復実行を観察するには、いくつかのパフォーマンス統計のためにクエリを実行することができます。 ここでは、クエリステートメント、作成時間、ステートメントの実行回数、およびクエリプランを取得したいと思います。

SELECT SUBSTRING(dest.text, (deqs.statement_start_offset / 2) + 1, (CASE deqs.statement_end_offset WHEN-1 THEN DATALENGTH(dest.text) ELSE deqs.statement_end_offset - deqs.statement_start_offset END) / 2 + 1) AS QueryStatement, deqs.creation_time, deqs.execution_count, deqp.query_planFROM sys.dm_exec_query_stats AS deqs CROSS APPLY sys.dm_exec_query_plan(deqs.plan_handle) AS deqp CROSS APPLY sys.dm_exec_sql_text(deqs.plan_handle) AS destWHERE deqp.objectid = OBJECT_ID('dbo.sfnGetRecentComment')ORDER BY deqs.last_execution_time DESC;GO

パフォーマンス統計の実行結果を次の画像で見ることができます。

iTVF vs Scalar DMV Comparison

重要なパフォーマンス指標を確認するもうひとつの方法は、拡張イベントを使用することです。

CREATE EVENT SESSION QuerySession ON SERVER ADD EVENT sqlserver.module_end( WHERE (.(.,(??)))), -- enter your Session Id ADD EVENT sqlserver.sp_statement_completed( WHERE (.(.,(??)))),-- enter your Session Id ADD EVENT sqlserver.sql_batch_completed( WHERE (.(.,(??)))); -- enter your Session IdGOALTER EVENT SESSION QuerySession ON SERVER STATE = START;GO

拡張イベント セッションが作成されたら、オブジェクト エクスプローラーの [Management (管理)] に移動し、セッションを展開して、作成したばかりの「QuerySession」のコンテキスト メニューを開きます。

新しく開いたウィンドウで、繰り返し実行されている証拠として、スカラー関数の複数のステートメント行を観察します。

赤い四角をクリックして、「データ フィードの停止」を行います。

MSTVF のパフォーマンス

MSTVF を使用した結果、パフォーマンスが低下することがありますが、これは不正確な統計情報のために SQL Server が最適ではない実行プランを選択することに関連しています。 SQL Serverは、内部で取得した統計情報をもとに、クエリが何行のデータを返すかを把握し、最適なプランを作成します。

SQL Server 2014 より前は、テーブル変数のカーディナリティの推定値は 1 行に設定されていました。

SQL Server 2014 以前は、テーブル変数のカーディナリティは 1 行とされていました。 MSTVFは、関数の定義の先頭で宣言されたテーブル変数を介してデータを返します。 現実には、オプティマイザーは時間に関係なく、これまでに見つけた最高のプランを求めることはなく、そこでバランスを保たなければなりません(非常に簡単に言えば)。

実際には、MSTVF を iTVF に置き換えると、iTVF が基礎となるテーブルの統計情報を使用するため、パフォーマンスが向上します。

以下のコードは、APPLY 演算子を使用して、Users テーブル全体に対して MSTVF を実行しています。 Users テーブルのすべての行に対して、Users テーブルの Id と、2008 年 1 月 1 日に設定されたコメントの日付を渡して、関数から LatestCommentDate を返しています。 スカラー関数の実行と同様に、これもコールドキャッシュから行います。

CHECKPOINT;DBCC DROPCLEANBUFFERS;DBCC FREEPROCCACHE;GODECLARE @CommentDate date= '2008-01-01T00:00:00.000';SELECT u.Id, c.LatestCommentDateFROM .Users u OUTER APPLY .(u.Id, @CommentDate) c;GO

結果の実行計画は次の画像のようになり、黄色のプロパティ ボックスでは、推定行数と実際の行数の値に注目してください。

table valued function からの推定行数は 100 に設定されていますが、実際の行数は 151,482 に設定されていることがわかります。

このように推定行数と実際の行数の値が大きく異なると、一般的にパフォーマンスが低下し、より最適なプランを得ることができず、最終的にはより良いクエリを実行できない可能性があります。

MSTVF 実行プランの比較

前回実行したスカラー関数と同様に、SQL Server のパフォーマンス統計に対するクエリを実行します。

SELECT SUBSTRING(dest.text, (deqs.statement_start_offset / 2) + 1, (CASE deqs.statement_end_offset WHEN-1 THEN DATALENGTH(dest.text) ELSE deqs.statement_end_offset - deqs.statement_start_offset END) / 2 + 1) AS QueryStatement, deqs.creation_time, deqs.execution_count, deqp.query_planFROM sys.dm_exec_query_stats AS deqs OUTER APPLY sys.dm_exec_query_plan(deqs.plan_handle) AS deqp OUTER APPLY sys.dm_exec_sql_text(deqs.plan_handle) AS destWHERE deqp.objectid = OBJECT_ID('dbo.mstvfnGetRecentComment');

最初に観測した MSTVF の実行プランは、関数が行う実際の作業を反映していないため、誤解を招く恐れがあります。 先ほど実行したパフォーマンス統計を使用し、Showplan XMLを選択すると、MSTVFが行っている作業を示す異なるクエリプランが表示されるはずです。 最初に見たものとは異なるプランの例を以下に示します。 ここでは、MSTVF が @RecentComment というテーブル変数にデータを挿入していることがわかります。

MSTVF Query Plan from DMVs

今回取得したパフォーマンス指標のもう 1 つの側面は、実行回数です。

以下の実行回数の画像では、76 という値が表示されていますが、これは MSTVF が複数回実行された証拠です。

MSTVF の複数回の実行

オブジェクト エクスプローラーの [管理] に戻り、セッションを展開して、先ほどスカラー パフォーマンスを見たときに作成した「QuerySessions」セッションの「Watch live data」を選択します。 セッションが以前に停止していた場合は、SSMS で「データフィードの開始」の緑の三角形をクリックすると、今回の例では、ステートメントの繰り返し実行を表す MSTVF の複数のステートメント行が表示されます。

まだ完了していない場合は、この時点で遠慮なくクエリをキャンセルしてください。

iTVF のパフォーマンス

スカラー関数またはマルチ ステートメント関数を iTVF で置き換える場合、実行時間や複数回の実行など、先ほど見たパフォーマンス上のペナルティを回避したいと考えています。 先ほどの例を参考に、APPLY 演算子を使って Users テーブルを照会し、iTVF から 2008 年 1 月 1 日以降の ID と最新コメントの日付を取得します。

--run in an isolated environment CHECKPOINT;DBCC DROPCLEANBUFFERS;DBCC FREEPROCCACHE;GODECLARE @CommentDate DATE= '2008-01-01T00:00:00.000';SELECT u.Id, c.LatestCommentDateFROM dbo.Users u OUTER APPLY .(u.Id, @CommentDate) c;GO

以下のクエリは、実行されているステートメント、実行カウント、およびそのステートメントのクエリ プランを確認するのに役立つパフォーマンス統計で構成されています。

-- Query stats and execution planSELECT SUBSTRING(dest.text, (deqs.statement_start_offset / 2) + 1, (CASE deqs.statement_end_offset WHEN-1 THEN DATALENGTH(dest.text) ELSE deqs.statement_end_offset - deqs.statement_start_offset END) / 2 + 1) AS QueryStatement, deqs.creation_time, deqs.execution_count, deqp.query_planFROM sys.dm_exec_query_stats AS deqs CROSS APPLY sys.dm_exec_query_plan(deqs.plan_handle) AS deqp CROSS APPLY sys.dm_exec_sql_text(deqs.plan_handle) AS destWHERE deqp.dbid = DB_ID('StackOverflow2010')ORDER BY deqs.last_execution_time DESC;

先ほど実行した iTVF のサンプル クエリは 4 秒強で完了し、問い合わせたパフォーマンス統計では、次の画像に示すように iTVF の実行カウントが 1 になっています。

iTVF DMV Stats

Comparing scalar, MSTVF and iTVF performance

次の表は、先ほど実行した 3 つの UDF の実行時間と実行回数の比較を示しています。

スカラーを比較しています。
スカラー MSTVF iTVF
実行時間 > 5分 > 5分 4秒
実行回数 > 10,000 > 10,000 1

iTVFを使用することによる実行時間と実行回数の違いは、すぐにわかります。 この例では、iTVFは同じデータをより速く、1回の実行で返しています。

限界

iTVF は、パフォーマンスを犠牲にすることなくロジックを再利用する必要がある場合に威力を発揮しますが、完璧ではありません。

一般的なユーザー定義関数の制限や制約のリストは、こちらの Books Online をご覧ください。

単一の SELECT ステートメント

iTVF の定義は 1 つの SELECT ステートメントに限定されているため、変数の割り当て、条件付きロジック、または一時テーブルの使用が必要な 1 つの SELECT ステートメントを超えるロジックを必要とするソリューションは、おそらく iTVF だけで実装するのには適していません。

エラー 処理

ストアド プロシージャの呼び出し

拡張ストアド プロシージャでない限り、iTVF の本体からストアド プロシージャを呼び出すことはできません。

結論

この記事は、2016年現在のインライン テーブル値関数 (iTVF) を再検討し、さまざまな方法で使用できることを示すことを目的として始まりました。

iTVFには、他のタイプのユーザー定義関数(UDF)とは異なり、パフォーマンスを犠牲にしないという重要な利点があり、再利用とカプセル化の機能により、優れた開発習慣を促進する可能性があります。

SQL Serverの最近の機能では、まずSQL Server 2017で、そして今後のSQL Server 2019では、スカラーおよびMSTVFのいずれかのパフォーマンスに、さらにいくつかの助けがあります。 詳しくは、MSTVFのカーディナリティを1または100という固定値ではなく、正確なカーディナリティの推定値を提供するインターリーブ実行や、スカラUDFのインライン化をご覧ください。 2019年のスカラUDFインライン化では、スカラUDFのパフォーマンス上の弱点に対処することに焦点が当てられています。

ソリューションについては、必ずテストを行い、その限界を認識してください。

お読みいただきありがとうございました。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です