数式項目のコンパイルサイズ制限をかいくぐる(前編)

2013年10月29日

Sandbox環境で数式を作成、いざ本番環境へ!...と意気込んでいたら、
「コンパイル後の文字サイズが5,000バイトを超えている」というエラーが。
どう見ても大した事をしていない数式が引っかかって驚く。

数式を作成する際のヒントによれば、

TEXT、DATEVALUE、DATETIMEVALUE、DATE など、一部の関数ではコンパイルサイズが著しく大きくなります。

だそうで。

もっと詳しく知りたいと思い検索したら、Tips for Reducing Formula Size(Last updated: October 19, 2013) PDF資料が検索に引っかかった。
どうやら日本語版はまだ出ていないようだ。

とりあえずPDF内の"Reducing Your Formula's Compile Size"を分かる程度に訳したモノを掲載しておく。
長いので、2エントリに分けることにする。

免責事項

当コンテンツの閲覧及び利用に伴い、何らかのトラブルや損失を負った場合などのいかなる損害も当方では一切の責任を負いません。利用者による自己責任の元でご参照ください。
当コンテンツにてご紹介しているウェブサイトやソフトウェアの合法性、正確性、最新性、適切性など、その内容については一切の保証を致しかねます。


数式のコンパイルサイズを減らす

数式のコンパイル結果はデータベースが実行出来る最大値によって制限されている。
この制限は全てのセールスフォースのエディションで同一で:コンパイル後の数式のサイズは(バイトで)5,000バイトだ。

この制限を超えた場合、"コンパイル後の文字列が大きすぎます (13,974 文字)。最大値は 5,000 文字です" というエラーメッセージが出る。
これはあなたが作成した数式のコンパイル結果が、データベースが一度に扱える最大値を越えているために起こる。

数式から減らしても意味がない項目:コメント、空白、フィールド名の長さ
これらを減らしたところでコンパイル後のサイズは変わらない。
数式を複数の項目に分けることもそう。それらの項目のサイズは、メインの数式のコンパイルサイズに含まれてしまうから。

幸運なことに、打つ手はあって、多くの場合は制限に引っかかる事無く数式を有用なものにできる。

  • 他の項目への参照数を最小限にする
  • 関数のコール数を最小限にする
  • 選択項目を再考する
  • 原因について違う風に考えてみる
  • それでもだめなら、ワークフローの項目自動更新を使う

他の項目への参照数を最小限にする

数式のコンパイル結果サイズを減らすために一番重要なのは、他の数式項目への参照数を減らすことだ。
項目を参照するたびに、その項目のコンパイルサイズが今作業している数式に足されていく。
日付項目のようなシンプルな項目はサイズが小さいが、他の数式項目はサイズが大きくなる可能性がある。

2つの項目について考えてみよう。

  • Date1__cは日付項目
  • Date2__cは、Date1__cを利用して作られる数式項目
DATE( YEAR( Date1__c ), MONTH( Date1__c ), DAY( Date1__c ) )

Date1__cを使用すると、それが参照されるごとに、コンパイル後の文字サイズには22文字が足される。
Date2__cの場合は毎回465文字だ。
「与えられた日付に対して、デッドラインとなる2営業日を足す」ものを生成する数式が与える影響を見てみよう。

CASE( MOD( SomeDate__c - DATE( 1900, 1, 7 ), 7 ),
0, SomeDate__c + 1 + 2, /* Sunday */
1, SomeDate__c + 2,
2, SomeDate__c + 2,
3, SomeDate__c + 2,
4, SomeDate__c + 2 + 2, /* Thursday */
5, SomeDate__c + 2 + 2, /* Friday */
6, SomeDate__c + 2 + 2, /* Saturday */
SomeDate__c             /* Default */
)

上の数式のSomeDate__cをDate1__cで置き換えると487文字がコンパイル結果に足される。
だが、SomeDate__c をDate2__cで置き換えると4,474文字に達する!
それらの内訳の多くは、9つの参照に占められる:Date2__c: 9 * 465 = 4,185 文字.

それで、どうやって他の項目への参照数を減らすんだ?

「デフォルト(負債=規定)」値をCASE()でレバレッジする

CASE()関数の最後の引数は、規定値だ。
もし複数のケースを同じ値で扱うなら、規定値として利用する事でチェック数を減らすことができる。
もう一度あの数式を見てみよう。

CASE( MOD( SomeDate__c - DATE( 1900, 1, 7 ), 7 ),
0, SomeDate__c + 1 + 2, /* Sunday */
1, SomeDate__c + 2,
2, SomeDate__c + 2,
3, SomeDate__c + 2,
4, SomeDate__c + 2 + 2, /* Thursday */
5, SomeDate__c + 2 + 2, /* Friday */
6, SomeDate__c + 2 + 2, /* Saturday */
SomeDate__c             /* Default */
)

この数式の規定値はSomeDate__cだ。
しかし、MOD( Date__c - DATE( 1900, 1, 7 ), 7 )の値は常に0から6で、規定値SomeDate__cは使われない。
この数式はこのように書き換えられる。

CASE( MOD( SomeDate__c - DATE( 1900, 1, 7 ), 7 ),
0, SomeDate__c + 1 + 2, /* Sunday */
4, SomeDate__c + 2 + 2, /* Thursday */
5, SomeDate__c + 2 + 2, /* Friday */
6, SomeDate__c + 2 + 2, /* Saturday */
SomeDate__c + 2         /* Default - Mon/Tues/Wed */
)

Monday/Tuesday/Wednesdayケースを規定値にすることで、Date1__cのコンパイル後のサイズは360文字、Date2__cのコンパイル後のサイズなら3,018文字まで減らすことができた。

ネストされたOR()の代わりにCASE()を使う

次の数式は、与えられた日付に対して、その月の最終日を返す。
(2月は常に28日あると仮定して(´Д⊂)

DATE(
YEAR( SomeDate__c ),
MONTH( SomeDate__c ),
IF(
OR(
MONTH( SomeDate__c ) = 4,
MONTH( SomeDate__c ) = 6,
MONTH( SomeDate__c ) = 9,
MONTH( SomeDate__c ) = 11
), 30, IF(
MONTH( SomeDate__c ) = 2,
28,
31
) )
)

この数式は最初に30日ある月をチェックして、次に2月をチェック、残りの月は31日とする。
これはネストされたIF()関数を必要とする。
読みやすいとは言いがたく、しかもコンパイル後にはDate1__cなら1069文字、Date2__cなら、なんと膨大な7,271文字にもなる!
なぜか?数式が日付を7回も参照しているからだ。
下記の改訂版と比べてみよう。

DATE(
YEAR( SomeDate__c ),
MONTH( SomeDate__c ),
CASE(
MONTH( SomeDate__c ),
2, 28,
4, 30,
6, 30,
9, 30,
11, 30,
31
) )

こちらの方が読みやすいだけでなく、コンパイル結果もDate1__cなら645文字、Date2__cなら3,309文字、そして項目の参照回数も7回から3回に減った。

ロジックを再編する

この例はsalesforce.com Answers communityから来たものだ。
この選択項目は担当者を選択肢としている。
数式では基本手数料と上乗せ手数料(乗数)を元に、手数料を計算している。
Base_Commission__cがそれぞれのCASE()内で参照されているため、この数式はコンパイル後の最大値を超えてしまった。

CASE( Agent__c,
"John", Base_Commission__c * 2,
"Jane", Base_Commission__c * 6,
/* Repeat for many other agents */
Base_Commission__c
)

これを修正するには、Base_Commission__cをCASE()関数の外に出そう。この数式は下記のように書き換えられる。

Base_Commission__c * CASE( Agent__c,
"John", 2,
"Jane", 6,
/* Repeat for many other agents */
1
)

基本手数料(Base_Commission__c)が通貨項目に過ぎず、数式でなかったとしても、何回も参照するより1度だけ参照するようにしたほうがはるかに数式のコンパイル後のサイズを減らせる。
もう一つの例として、あの営業日の数式でこれを試してみよう。

SomeDate__c + CASE( MOD( SomeDate__c - DATE( 1900, 1, 7 ), 7 ),
0, 1 + 2, /* Sunday */
4, 2 + 2, /* Thursday */
5, 2 + 2, /* Friday */
6, 2 + 2, /* Saturday */
2         /* Default - Mon/Tues/Wed */
)

とうとう、Date1__cなら188文字、Date2__cなら1,074文字までサイズを減らす事ができた、これは元の数式サイズの4分の1近くだ。


後編へ続く(はず)。

参考サイト