動的SOQLでのバインド変数の使い方

今回は動的SOQLとバインド変数について、実は動的SOQLでもバインド変数が使えるんですが、好き勝手使える訳ではないので、使えるケース、使えないケースを検証しました。

スポンサーリンク

動的SOALの基本的な使い方

動的SOQLでもバインド変数を使用することができます。
まずは基本パターンから。
※なお、今回のコードは全てOpen Execute Anonymous Windowで使用可能な内容になっています。

バインド変数を使用した動的SOQLの基本パターン

Savepoint sp = Database.setSavepoint();
// 検索用に1件データを登録
Account a = new Account(Name='Hoge');
insert a;

String serchStr = 'Hoge';
String q = 'Select Id, Name From Account Where Name = :serchStr';
// クエリーの内容を表示
System.debug(q);
List<sObject> objList = Database.query(q);
// 先ほどInsertしたAccountが登録されているか確認
System.debug(objList.size());

Database.rollback(sp);

■デバッグログ(クエリー)

Select Id, Name From Account Where Name = :serchStr

■デバッグログ(件数)

1

デバッグログ上のクエリーはバインド変数が残ったままですが、ちゃんと検索ができているのが分かります。

リスト型には使えるのか

次にリスト型が使えるかどうかの検証、

バインド変数をリスト型にした時のSOQL

Savepoint sp = Database.setSavepoint();
// 検索用に1件データを登録
Account a1 = new Account(Name='Hoge1');
insert a1;
Account a2 = new Account(Name='Hoge2');
insert a2;

String serchStr = 'Hoge';
List<String> serchStrList = new List<String>();
serchStrList.add('Hoge1');
serchStrList.add('Hoge2');
String q = 'Select Id, Name From Account Where Name IN :serchStrList';
// クエリーの内容を表示
System.debug(q);
List<sObject> objList = Database.query(q);
// 先ほどInsertしたAccountが登録されているか確認
System.debug(objList.size());

Database.rollback(sp);

■デバッグログ(クエリー)

Select Id, Name From Account Where Name IN :serchStrList

■デバッグログ(件数)

2

うん、これも検索されているので問題なく使える。

オブジェクト型では使えるのか

オブジェクトの項目をバインド変数にしたSOQL

Savepoint sp = Database.setSavepoint();
// 検索用に1件データを登録
Account a = new Account(Name='Hoge');
insert a;
String q = 'Select Id, Name From Account Where Id = :a.id';
// クエリーの内容を表示
System.debug(q);
List<sObject> objList = Database.query(q);
// 先ほどInsertしたAccountが登録されているか確認
System.debug(objList.size());

Database.rollback(sp);

エラーが出ました

Line: 8, Column: 1
System.QueryException: Variable does not exist: a.id

動的SOQLのバインドでオブジェクトの項目を拾うことはできないみたい

クラスのプロパティは

クラスのプロパティをバインドした動的SOQL

Savepoint sp = Database.setSavepoint();
// 検索用に1件データを登録
Account a = new Account(Name='Hoge');
insert a;
// クラスに検索文字列を入れる
HogeHoge hg = new HogeHoge('Hoge');
System.debug(hg.sertchStr);
String q = 'Select Id, Name From Account Where Name = :hg.sertchStr';
// クエリーの内容を表示
System.debug(q);
List<sObject> objList = Database.query(q);
// 先ほどInsertしたAccountが登録されているか確認
System.debug(objList.size());
Database.rollback(sp);
// 検索用クラス
private class HogeHoge{
    private String sertchStr;
    private HogeHoge(String s){
        sertchStr = s;
    }
}

これもダメでした。

Line: 11, Column: 1
System.QueryException: Variable does not exist: hg.sertchStr

クエリ部分を別メソッドで作った時は

クエリを別メソッド化した動的SOQL

Savepoint sp = Database.setSavepoint();
// 検索用に1件データを登録
Account a = new Account(Name='Hoge');
insert a;
String searchStr = 'Hoge';
String q = getQuery(searchStr);
// クエリーの内容を表示
System.debug(q);
List<sObject> objList = Database.query(q);
// 先ほどInsertしたAccountが登録されているか確認
System.debug(objList.size());
Database.rollback(sp);

// 検索用クラス
private String getQuery(String searchStr){
    String q = 'Select Id, Name From Account Where Name = :searchStr';
    return q;
}

■デバッグログ(クエリー)

Select Id, Name From Account Where Name = :searchStr

■デバッグログ(件数)

1

これはイケました。
でも、5行目の変数名searchStrを別の変数名にすると、

Line: 9, Column: 1
System.QueryException: Variable does not exist: searchStr

この通り怒られます。Database.queryを実行したタイミングで変数searchStrが存在しないから置換ができないんですね。
どうやら、クエリを作成するタイミングじゃなくて、実行するタイミングでバインド変数がそのメソッド内で存在している変数でないと変換ができないみたいです。

まとめ

  • IntegerやString型などのいわゆるプリミティブ型に対してはバインド可能
  • プリミティブ型のリストに対してもバインド可能
  • オブジェクトの項目やクラスのプロパティに対しての参照ではバインドできない
  • Database.query実行時点でバインド変数が解決可能となっていないとエラーになる

コメント

  1. MY より:

    動的SOQLの使用を確認していた時にたどり着きました。自分が知りたかったそのものずばりではないですが、段階を一歩一歩試しての記述が思考をなぞっているようでとてもわかりやすくよかったです。

    Dynamic SOQL
    https://developer.salesforce.com/docs/atlas.ja-jp.234.0.apexcode.meta/apexcode/apex_dynamic_soql.htm

    escapeSingleQuotes(stringToEscape)
    https://developer.salesforce.com/docs/atlas.ja-jp.apexcode.meta/apexcode/apex_methods_system_string.htm#apex_System_String_escapeSingleQuotes

    こちらのDeveloper’s GuideではSQL injectionについてそっけなく書いているだけなのでいいのかな?と探していたところです。