駆け出しsalesforce開発者として、なんとなく併用していたDatabase.query(String)とインラインSOQLに関してもう少しまじめに調べてみました。
前提:インラインSOQLの方が望ましい
調べてみると基本的にはインラインSOQLを使った方がいいとのこと。
【Apex】Database.query(String) を使ってはいけない
Database.query(String) の問題としては
1.セキュリティリスクを考慮する必要がある
2.エディタの補助機能を活かせない
3.項目の使用場所に引っ掛からない
があり、自分も 2 が理由で自然とインラインSOQLを好んでいました。
疑問:インラインSOQLが使えない場面はあるのか
しかしいままでは既存のコードがDatabase.query(String)を使っている部分はとりあえずそれに倣いクエリ文の中身だけ変更する、といった場当たり的な対応をしていました。その中で実際にインラインSOQLに変更できない場面はあるのか、現時点で思いついたのは以下の2つです(経験を積めばもっと事例が浮かぶかもしれませんが)。
1.バッチのクエリロケータの場合
こんな書き方がされていたので文字列で設定する形じゃないとダメなのかと思いましたが
private Database.QueryLocator getQueryLocator() {
String query = ' SELECT ~ ';
return Database.getQueryLocator(query);
}
調べたらこういう書き方もできました。
private Database.QueryLocator getQueryLocator() {
return Database.getQueryLocator([SELECT ~]);
}
インラインSOQLにおける [ ]=ListやSObject だと思っていましたが、この場合 [ ]=String という扱いなのか??
ともあれクエリロケータに限らずクエリを文字列で渡す、という場合にはこの方法でいけそうです。
2.取得項目を変数で渡す場合
「大量の項目を一度に設定」や「ループや条件分岐などによって項目を動的に設定」等の場合です。Database.query(String)を使っているのは特にこのパターンが多かったので、本丸の検証といえます。
以下のように書いてみると…
Schema.SObjectType objType = Account.SObjectType;
Schema.DescribeSObjectResult objResult = objType.getDescribe();
Map<String, Schema.SObjectField> fieldMap = objResult.fields.getMap();
String columns = String.join(new List<String>(fieldMap.keySet()), ', ');
Account acc = [
SELECT
:columns
FROM
Account
LIMIT 1
];
…ダメでした。
accでエラーが出ましたがVSCode上で見ると「:(バインド変数)」がこの位置では使えないようです。インラインSOQLでは項目とオブジェクトは直に書く必要がある、だからこそ前提で挙げた諸メリットを享受できるということでしょう。