FUNCTION RESULT_CACHE は、PL/SQL関数の戻り値をSGA上の結果キャッシュに保存し、同じ引数で呼び出されたときに再計算を避けるための仕組みです。計算コストが高く、入力に対して結果が安定している関数では、大きな効果が出ることがあります。
ただし、付ければ必ず速くなる機能ではありません。セッション依存値、NLS、権限、頻繁に更新される表、軽すぎる関数、RAC環境などでは、むしろ扱いづらくなることがあります。Result Cache全体の仕組みは Oracle Result Cache完全ガイド、関数設計全般は プロシージャ・ファンクション完全ガイド も参考になります。
FUNCTION RESULT_CACHEの基本構文- 使ってよい関数・使ってはいけない関数の判断基準
RELIES_ONの現行Oracleでの扱いDBMS_RESULT_CACHEとV$RESULT_CACHE_OBJECTSの確認SQL- 実行時間の測定、キャッシュヒット確認、フラッシュ方法
- RAC、セッション依存値、DML無効化、NLSの注意点
FUNCTION RESULT_CACHEとは
RESULT_CACHE を付けたPL/SQL関数は、同じ引数で呼び出された場合に、前回の戻り値を再利用できます。Oracle公式ドキュメントでも、結果はSGA上にキャッシュされ、同じインスタンスに接続するセッションで再利用できると説明されています。
基本構文
関数定義に RESULT_CACHE を付けます。まずは、入力値だけで結果が決まる単純な例で動きを確認します。
CREATE OR REPLACE FUNCTION calc_tax( p_amount IN NUMBER ) RETURN NUMBER RESULT_CACHE IS BEGIN RETURN ROUND(p_amount * 0.10); END; / SELECT calc_tax(1000) FROM dual; SELECT calc_tax(1000) FROM dual; -- 同じ引数ならキャッシュ再利用候補
ただし、このように軽い計算ではキャッシュの管理コストの方が大きい可能性があります。Result Cacheは、重い計算、頻繁な参照、結果の安定性がそろったときに検討します。
使ってよい関数の条件
Result Cache向きの関数は、同じ入力なら同じ結果を返し、かつ再計算コストが高い関数です。参照するデータが頻繁に変わらず、複数セッションから同じ値で呼ばれるほど効果が出ます。
使ってはいけない関数
Result Cacheに向かない関数もあります。特に、セッションや時刻、NLS、アプリケーションコンテキスト、外部状態に依存する関数は注意が必要です。
-- NG例: 時刻に依存する
CREATE OR REPLACE FUNCTION today_label
RETURN VARCHAR2
RESULT_CACHE
IS
BEGIN
RETURN TO_CHAR(SYSDATE, 'YYYY-MM-DD');
END;
/
-- NG例: セッション依存値に依存する
CREATE OR REPLACE FUNCTION current_tenant
RETURN VARCHAR2
RESULT_CACHE
IS
BEGIN
RETURN SYS_CONTEXT('APP_CTX', 'TENANT_ID');
END;
/
セッション依存の高速化は、Result Cacheではなく、パッケージ変数のセッション内キャッシュや通常の索引設計を検討します。アプリケーションコンテキストを使う設計は DBMS_SESSION.SET_CONTEXTの利用例 と合わせて考えると整理しやすいです。
マスタ参照関数の実装例
Result Cacheが効きやすい例として、更新頻度の低いマスタから表示名を引く関数があります。頻繁にSQL内で呼ばれるが、マスタは日中ほとんど変わらない、という条件なら候補になります。
CREATE TABLE m_status (
status_code VARCHAR2(20) PRIMARY KEY,
status_name VARCHAR2(100) NOT NULL
);
CREATE OR REPLACE FUNCTION get_status_name(
p_status_code IN VARCHAR2
) RETURN VARCHAR2
RESULT_CACHE
IS
l_status_name m_status.status_name%TYPE;
BEGIN
SELECT status_name
INTO l_status_name
FROM m_status
WHERE status_code = p_status_code;
RETURN l_status_name;
EXCEPTION
WHEN NO_DATA_FOUND THEN
RETURN NULL;
END;
/
SELECT order_id,
get_status_name(status_code) AS status_name
FROM orders;
SQL内でPL/SQL関数を呼ぶ設計は、関数呼び出しコストも含めて測る必要があります。SQL内PL/SQL関数の扱いは WITH FUNCTION句の使い方 も関連します。
RELIES_ONの扱い
古いサンプルでは RESULT_CACHE RELIES_ON(table_name) を見かけます。ただし、現行のOracleでは、関数実行中に問い合わせた表やビューなどの依存データソースをデータベース側が自動検出します。Oracle 12c以降では RELIES_ON は実質的に意味を持たない扱いなので、新規コードでは基本的に書かない方針でよいです。
-- 古い11g系サンプルで見かける形 CREATE OR REPLACE FUNCTION get_customer_sales(p_customer_id NUMBER) RETURN NUMBER RESULT_CACHE RELIES_ON (orders) IS l_total NUMBER; BEGIN SELECT NVL(SUM(amount), 0) INTO l_total FROM orders WHERE customer_id = p_customer_id; RETURN l_total; END; / -- 現行設計では通常 RELIES_ON を書かず、依存関係の自動検出に任せる CREATE OR REPLACE FUNCTION get_customer_sales(p_customer_id NUMBER) RETURN NUMBER RESULT_CACHE IS l_total NUMBER; BEGIN SELECT NVL(SUM(amount), 0) INTO l_total FROM orders WHERE customer_id = p_customer_id; RETURN l_total; END; /
キャッシュ状態を確認する
Result Cacheを使ったら、効いているか確認します。関数に句が付いているか、キャッシュオブジェクトが作られているか、統計上ヒットしているかを見ます。
-- 関数にRESULT_CACHEが付いているか
SELECT object_name,
procedure_name,
result_cache
FROM user_procedures
WHERE result_cache = 'YES'
ORDER BY object_name, procedure_name;
-- キャッシュされているオブジェクト
SELECT id,
type,
status,
name,
namespace,
creation_timestamp
FROM v$result_cache_objects
ORDER BY creation_timestamp DESC;
-- 統計情報
SELECT name, value
FROM v$result_cache_statistics
ORDER BY name;
V$RESULT_CACHE_OBJECTS や V$RESULT_CACHE_STATISTICS の参照には権限が必要です。権限がない開発環境では、DBAに確認してもらうか、検証用スキーマで参照権限を付与してもらいます。
DBMS_RESULT_CACHEで管理する
結果キャッシュは DBMS_RESULT_CACHE でフラッシュやメモリレポートを実行できます。本番でむやみに全フラッシュすると他処理にも影響するため、検証環境で使い方を確認しておきます。
-- キャッシュ全体のフラッシュ BEGIN DBMS_RESULT_CACHE.FLUSH; END; / -- メモリ使用状況のレポート SET SERVEROUTPUT ON BEGIN DBMS_RESULT_CACHE.MEMORY_REPORT; END; /
Result Cacheの詳細な管理やSQLヒント側の使い方は Result Cache完全ガイド 側で確認できます。パフォーマンス改善全体の位置づけは PL/SQLパフォーマンス改善プレイブック も参考になります。
実行時間を測定する
Result Cacheは、体感で判断せず測定します。同じ引数で1回目と2回目を呼び、統計や経過時間を比較します。ただし、SQLキャッシュ、バッファキャッシュ、実行計画キャッシュの影響もあるため、単純な1回比較だけで結論を出さないようにします。
SET SERVEROUTPUT ON
DECLARE
l_start PLS_INTEGER;
l_value NUMBER;
BEGIN
DBMS_RESULT_CACHE.FLUSH;
l_start := DBMS_UTILITY.GET_TIME;
l_value := get_customer_sales(1001);
DBMS_OUTPUT.PUT_LINE('first=' || (DBMS_UTILITY.GET_TIME - l_start));
l_start := DBMS_UTILITY.GET_TIME;
l_value := get_customer_sales(1001);
DBMS_OUTPUT.PUT_LINE('second=' || (DBMS_UTILITY.GET_TIME - l_start));
END;
/
本格的にボトルネックを確認する場合は、Result Cacheだけでなくプロファイラで関数呼び出し回数や実行時間を確認します。行単位の分析は PL/SQLプロファイラ完全ガイド や DBMS_PROFILERで行レベル分析する完全ガイド が役立ちます。
DMLによる無効化を理解する
Result Cacheは、関数が問い合わせた表にDMLがコミットされると、関連するキャッシュが無効化されます。つまり、更新頻度が高い表を参照する関数では、キャッシュしてもすぐ無効になり、効果が出にくくなります。
-- 1回目: キャッシュ作成 SELECT get_customer_sales(1001) FROM dual; -- 2回目: キャッシュ再利用候補 SELECT get_customer_sales(1001) FROM dual; -- 参照表を更新してコミット UPDATE orders SET amount = amount + 100 WHERE customer_id = 1001; COMMIT; -- 関連キャッシュは無効化され、再計算される SELECT get_customer_sales(1001) FROM dual;
RAC環境での注意
RAC環境では、インスタンスごとのキャッシュ、無効化通知、メモリ使用量を意識します。同じ関数でも、どのインスタンスで呼ばれるかによってキャッシュヒットの状況が変わることがあります。本番では単一インスタンスの検証結果だけで判断せず、接続先分散やサービス構成も含めて確認します。
よくある失敗
軽い関数に付けて逆に遅くなる
文字列連結や単純計算のような軽い関数では、キャッシュ管理コストの方が目立つことがあります。Result Cacheは重い処理に絞って使います。
ユーザーごとに結果が違う関数へ付ける
同じ引数でもユーザー、テナント、ロール、NLSで結果が変わる関数は危険です。セッション依存値を読む関数では、キャッシュキーにその値が入らない前提で慎重に判断します。
更新頻度が高い表に使う
DMLで無効化されるため、常に更新されるテーブルではキャッシュヒットより無効化が多くなりがちです。この場合は索引、SQL改善、集計テーブル、マテリアライズドビューを検討します。Result Cacheとマテリアライズドビューの併用は Result Cacheとマテリアライズドビューを併用した高速化戦略 も関連します。
本番前チェックリスト
SYS_CONTEXT、NLS、CURRENT_USER、SYSDATEに依存していないか。V$RESULT_CACHE_STATISTICS でヒット率やメモリを確認できるか。RESULT_CACHE_MAX_SIZE と共有リソースへの影響を確認したか。まとめ
FUNCTION RESULT_CACHE は、同じ入力で同じ結果を返す高コストなPL/SQL関数に対して、再計算を避けるための有効な選択肢です。一方で、セッション依存値、時刻、NLS、副作用、更新頻度の高い表に依存する関数には向きません。
実務では、まずプロファイラや実行時間測定で本当に関数がボトルネックか確認し、Result Cache向きの条件を満たす関数にだけ適用します。RELIES_ON は古いサンプルとして見かけますが、現行Oracleでは依存関係の自動検出を前提にし、確認SQLと運用監視まで含めて導入しましょう。

