はじめに
本投稿の元ネタは、こちらのブログです。
内容を簡単に書くと「項目から日付を持ってきて、その月に日曜日が何回あるかを算出する」とのことで、以下の数式が紹介されていました。
4 + IF( AND( DAY(LastDay__c)>=29, WEEKDAY( DATE( YEAR( BaseDay__c ), MONTH( BaseDay__c ), 29) ) = 1), 1, 0) + IF( AND( DAY(LastDay__c)>=30, WEEKDAY( DATE( YEAR( BaseDay__c ), MONTH( BaseDay__c ), 30) ) = 1), 1, 0) + IF( AND( DAY(LastDay__c)>=31, WEEKDAY( DATE( YEAR( BaseDay__c ), MONTH( BaseDay__c ), 31) ) = 1), 1, 0)
数式の意味についてはリンク先をご覧いただければと思いますが、上記数式のうち、
DATE( YEAR( BaseDay__c ), MONTH( BaseDay__c ), 31)
を見たときにこう思いました。
「この数式、BaseDay__c
次第ではエラーになるのでは?」
(でも、さすがにブログに書く前に動作確認はしているよなぁ…)と思いつつ、「よく考えたら、AND、ORの条件判定文でエラーが入ったときにどういう処理が行われるか知らないわ」と思ったので、調査することにしました。
AND関数にエラー数式を混ぜる
まず、日付を設定する項目 BaseDay__c
と、 DATE( YEAR( BaseDay__c ), MONTH( BaseDay__c ), 29)
、DATE( YEAR( BaseDay__c ), MONTH( BaseDay__c ), 30)
、DATE( YEAR( BaseDay__c ), MONTH( BaseDay__c ), 31)
の数式項目を作成します。
そして、BaseDay__c
に様々な日付を設定し、数式項目の結果を確認します。
予想通り、存在しない日付をDATE関数で出力しようとするとエラーになりました。
続いて、ブログを参考に LastDay__c
を設定します。
そして、ブログに記載されていた以下数式を少し変更し、月末が異なる日付で出力させてみます。
- 29日は日曜日?:
AND( DAY(LastDay__c)>=29, WEEKDAY( DATE( YEAR( BaseDay__c ), MONTH( BaseDay__c ), 29) ) = 1)
- 30日は日曜日?:
AND( DAY(LastDay__c)>=30, WEEKDAY( DATE( YEAR( BaseDay__c ), MONTH( BaseDay__c ), 30) ) = 1)
- 31日は日曜日?:
AND( DAY(LastDay__c)>=31, WEEKDAY( DATE( YEAR( BaseDay__c ), MONTH( BaseDay__c ), 31) ) = 1)
AND関数の引数にエラーになる数式が入っているはずですが、数式全体としてはエラーになりませんでした。
次に、AND関数の引数の順番を入れ替えます。
- 29日は日曜日?:
AND( WEEKDAY( DATE( YEAR( BaseDay__c ), MONTH( BaseDay__c ), 29) ) = 1, DAY(LastDay__c)>=29)
- 30日は日曜日?:
AND( WEEKDAY( DATE( YEAR( BaseDay__c ), MONTH( BaseDay__c ), 30) ) = 1, DAY(LastDay__c)>=30)
- 31日は日曜日?:
AND( WEEKDAY( DATE( YEAR( BaseDay__c ), MONTH( BaseDay__c ), 31) ) = 1, DAY(LastDay__c)>=31)
論理式であれば X AND Y
と Y AND X
は同じ結果になるはずですが、どうでしょうか。
今度は存在しない日付の場合はエラーを表示するようになりました。
さらに最初のAND数式を少し変更し、「AND関数の最初の引数は必ずTRUEを返す数式」を設定します。
- 29日は日曜日?:
AND( DAY(LastDay__c)>=28, WEEKDAY( DATE( YEAR( BaseDay__c ), MONTH( BaseDay__c ), 29) ) = 1)
- 30日は日曜日?:
AND( DAY(LastDay__c)>=28, WEEKDAY( DATE( YEAR( BaseDay__c ), MONTH( BaseDay__c ), 30) ) = 1)
- 31日は日曜日?:
AND( DAY(LastDay__c)>=28, WEEKDAY( DATE( YEAR( BaseDay__c ), MONTH( BaseDay__c ), 31) ) = 1)
これは、「LastDay__c
の日は28以上である」という、BaseDay__c
にどの日付を入れても必ずTRUEになる数式をAND変数の1番目の引数に設定しています。
エラー数式をAND関数の1番目の引数に設定した場合と同じ動作でした。
AND関数の動作推察
このことから、以下の動作になっていると推測できます。
- AND関数は左の引数からTRUE、FALSEを判定している。
- 引数の途中でFALSEと判定された場合は、そこで判定を打ち切り、AND関数はFALSEを返す。
- 最初の日曜日判定で存在しない日付でもエラーにならなかった理由は、
DAY(LastDay__c)>=XX
の時点でFALSEと判定され、その後のエラー数式が判定されなかったためと推測できます。
- 最初の日曜日判定で存在しない日付でもエラーにならなかった理由は、
- 引数の途中でエラーと判定された場合は、そこで判定を打ち切り、AND関数はエラーを返す。
- AND関数の全ての引数がTRUEの場合はTRUEを返す。
OR関数にエラー数式を混ぜる
今度は、OR関数の1番目の引数に必ずTRUEになる数式と必ずFALSEになる数式を設定し、2番目の引数にエラー数式を設定して動作を見てみます。
1番目の引数に必ずTRUEになる数式を設定
- 29日は日曜日?:
OR( DAY(LastDay__c)>=28, WEEKDAY( DATE( YEAR( BaseDay__c ), MONTH( BaseDay__c ), 29) ) = 1)
- 30日は日曜日?:
OR( DAY(LastDay__c)>=28, WEEKDAY( DATE( YEAR( BaseDay__c ), MONTH( BaseDay__c ), 30) ) = 1)
- 31日は日曜日?:
OR( DAY(LastDay__c)>=28, WEEKDAY( DATE( YEAR( BaseDay__c ), MONTH( BaseDay__c ), 31) ) = 1)
動作結果は以下になりました。
OR関数は引数のうち1つでもTRUEがあればTRUEを返すため、期待通りの動作と言えます。
1番目の引数に必ずFALSEになる数式を設定
- 29日は日曜日?:
OR( DAY(LastDay__c)>=32, WEEKDAY( DATE( YEAR( BaseDay__c ), MONTH( BaseDay__c ), 29) ) = 1)
- 30日は日曜日?:
OR( DAY(LastDay__c)>=32, WEEKDAY( DATE( YEAR( BaseDay__c ), MONTH( BaseDay__c ), 30) ) = 1)
- 31日は日曜日?:
OR( DAY(LastDay__c)>=32, WEEKDAY( DATE( YEAR( BaseDay__c ), MONTH( BaseDay__c ), 31) ) = 1)
動作結果は以下になりました。
AND関数にて検証した、「AND数式の最初の引数は必ずTRUEを返す数式」を設定したときと同じ動作でした。
OR関数の動作推察
このことから、以下の動作になっていると推測できます。
- OR関数は左の引数からTRUE、FALSEを判定している。
- 引数の途中でTRUEと判定された場合は、そこで判定を打ち切り、OR関数はTRUEを返す。
- 引数の途中でエラーと判定された場合は、そこで判定を打ち切り、OR関数はエラーを返す。
- OR関数の全ての引数がFALSEの場合はFALSEを返す。
終わりに
今回の検証にて確認できた、「AND、OR関数にて引数の途中で結果が確定した場合は、最後まで処理を行わずに結果を返す」動作は「短絡評価」といい、Salesforce外のプログラミング言語でも使われています。
Salesforce数式を作成するうえではあまり気にしなくてもよい動作かと思いますが*1、「こういう動作なんだ」ということを頭の片隅にでも入れていただければ幸いです。
*1:条件式にエラーになりえる数式を積極的に使う場合は必要ですが…