私の過去の経験では、複雑なロジックをあまり使用せずにデータセットを選択する必要があり、パラメーターも渡す必要がある単純なケースで常に関数を使用していました。
最近、MSSQLで関数を使用することは絶対に避けなければならないという通知を受けました。これは、関数を使用するとパフォーマンスの問題が発生することが多く、使用するとインデックスが適切に使用されない場合があるためです。誰かがこの点について話し、これが真実であるかどうか、そしてその背後にあるいくつかの理由をさらに詳細に説明できますか?
私の過去の経験では、複雑なロジックをあまり使用せずにデータセットを選択する必要があり、パラメーターも渡す必要がある単純なケースで常に関数を使用していました。
最近、MSSQLで関数を使用することは絶対に避けなければならないという通知を受けました。これは、関数を使用するとパフォーマンスの問題が発生することが多く、使用するとインデックスが適切に使用されない場合があるためです。誰かがこの点について話し、これが真実であるかどうか、そしてその背後にあるいくつかの理由をさらに詳細に説明できますか?
あなたは素朴にアドバイスされました。
スカラー関数
WHERE dbo.fn_get_year(tbl.field) = 2012
難読化tbl.field
して、その上のインデックスを使用できなくします。
たとえば、 を使用すると、はるかに優れたパフォーマンスが得られますWHERE tbl.field >= '20120101' AND tbl.field < '20130101'
。
最初の例では、すべてのレコードを処理する必要があります。これは、オプティマイザーが関数を見て、どの範囲のレコードが基準に適合するかを推測できないためです。
2 番目の例では、ポイント a からポイント b まで連続したレコード ブロックが必要であることを明確に示しています。これにより、オプティマイザはレンジ シークにインデックスを使用できるようになります。
テーブル値関数
そのすべてが とは大きく異なりSELECT * FROM dbo.my_function(@parameter) AS data
ます。テーブル値関数がそのように使用されても問題はありません。
関数の結果を別のテーブルまたは関数に結合すると複雑になります。
関数が複数ステートメント(IF
ブロックなどを含む)の場合、結合が処理される前に関数の結果セット全体が返されます。
関数がインライン関数( のみRETURNS TABLE AS SELECT blah FROM blah
)である場合、SQL Server はそれをマクロとして扱います(そうしないように指示しない限り)。これは、関数コードがクエリに置き換えられ、クエリに対してまったく新しい実行プランが構築されることを意味します。これは、インデックスの最適化などのために、関数の関連レコードのみが処理されることを意味する場合があります。
要するに、あなたにアドバイスをした人に、彼らのアドバイスについて非常に具体的にするように依頼してください. 残っている場合はnever use functions
無視してください。
IMOアンチプラクティスは、クエリのWHERE句でスカラー関数を使用することです。SQLに優れた選択性を与える他のフィルターは使用しません。
例えば
SELECT columns
FROM [table]
WHERE dbo.myFunc(col1) = 55
のインデックス作成に関係なく、通常はテーブル スキャンが実行されcol1
ます。
他の人が指摘したように、例外があります。たとえば、インデックス付きの計算列で決定論的でスキーマにバインドされた関数を使用できます。
例として、次の決定論的関数を考えてみましょう。
CREATE FUNCTION dbo.myFunc(@id int)
returns int
WITH SCHEMABINDING
AS
BEGIN
return (@id + 1)
END
与えられたテーブル (MSSQL の既定の PK = クラスター化インデックス)
CREATE TABLE MyTable
(
ID INT Identity (1,1),
SomeOtherColumn VARCHAR(50),
CONSTRAINT PK_MyTable PRIMARY KEY(ID)
)
約 10 万件のレコードが入力されています
select * from MyTable where ID < 100 -- Index Seek :)
ただし、スカラー関数を実行しても、クラスター化インデックスの利点は得られません。
select * from MyTable where dbo.MyFunc(Id) < 100 -- Index Scan :(
計算列の基礎としてスカラー関数を使用する
alter table MyTable add Computed as dbo.MyFunc(ID)
select * from MyTable where Computed < 100 -- Still Index Scan :(
-- ただし、Computed 列は決定論的であり、スキーマにバインドされているため、インデックスを作成できます。
CREATE INDEX IX1_MyTable on MyTable(Computed)
select * from MyTable where Computed < 100 -- Index Seek :)
興味深いことに、関数を適用するとインデックス シークが発生するようになりました (SQL 2008R2)。
select * from MyTable where dbo.MyFunc(ID) < 100 -- Index Seek :)