Database.query メソッドと動的SOQL

Salesforce の `Database.query` メソッドは、SOQL クエリを動的に組み立てて実行できる便利な方法です。特に、検索条件を動的に変更したい場合や、クエリを組み立てる必要があるケースで使用されます。

---

## **基本構文**
```apex
List<SObject> results = Database.query(String query);
```
- `query` には **SOQL 文字列** を指定する。
- `List<SObject>` 型で結果を取得するため、型キャストが必要な場合がある。

---

## **静的 SOQL と 動的 SOQL の違い**
```apex
// 静的 SOQL
List<Account> accounts = [SELECT Id, Name FROM Account WHERE Industry = 'Technology'];

// 動的 SOQL
String industryType = 'Technology';
String query = 'SELECT Id, Name FROM Account WHERE Industry = \'' + industryType + '\'';
List<Account> accounts = Database.query(query);
```
**動的 SOQL のメリット:**
- `industryType` の値を動的に変更できるため、柔軟な検索が可能。
- `WHERE` 条件や `ORDER BY` などを組み合わせた検索を動的に構築できる。

---

## **動的 SOQL の具体例**
### **1. ユーザー入力に基づく動的検索**
たとえば、`Industry` と `Rating` を入力してアカウントを検索する場合：
```apex
public List<Account> searchAccounts(String industry, String rating) {
    String query = 'SELECT Id, Name, Industry, Rating FROM Account WHERE Industry = \'' + industry + '\'';

    if (rating != null && rating != '') {
        query += ' AND Rating = \'' + rating + '\'';
    }

    return Database.query(query);
}
```
**使用例**
```apex
List<Account> result = searchAccounts('Technology', 'Hot');
```
この場合、以下のクエリが実行される：
```sql
SELECT Id, Name, Industry, Rating FROM Account WHERE Industry = 'Technology' AND Rating = 'Hot'
```
動的に `AND Rating = 'Hot'` の条件が追加される。

---

### **2. LIKE を使ったあいまい検索**
たとえば、`Name` に特定のキーワードを含むアカウントを検索する場合：
```apex
public List<Account> searchByName(String keyword) {
    String query = 'SELECT Id, Name FROM Account WHERE Name LIKE \'%' + keyword + '%\'';
    return Database.query(query);
}
```
**使用例**
```apex
List<Account> result = searchByName('Tech');
```
実行されるクエリ：
```sql
SELECT Id, Name FROM Account WHERE Name LIKE '%Tech%'
```

---

### **3. ORDER BY で並び替え**
```apex
public List<Contact> getSortedContacts(String sortBy) {
    String query = 'SELECT Id, FirstName, LastName FROM Contact ORDER BY ' + sortBy;
    return Database.query(query);
}
```
**使用例**
```apex
List<Contact> contacts = getSortedContacts('LastName');
```
実行されるクエリ：
```sql
SELECT Id, FirstName, LastName FROM Contact ORDER BY LastName
```

---

### **4. LIMIT と OFFSET を使ったページネーション**
```apex
public List<Opportunity> getOpportunities(Integer pageNumber, Integer pageSize) {
    Integer offsetValue = (pageNumber - 1) * pageSize;
    String query = 'SELECT Id, Name, Amount FROM Opportunity ORDER BY CloseDate DESC LIMIT ' + pageSize + ' OFFSET ' + offsetValue;
    return Database.query(query);
}
```
**使用例**
```apex
List<Opportunity> opportunities = getOpportunities(2, 10);
```
実行されるクエリ（2ページ目、10件表示）：
```sql
SELECT Id, Name, Amount FROM Opportunity ORDER BY CloseDate DESC LIMIT 10 OFFSET 10
```

---

## **動的 SOQL の注意点**
1. **SOQL インジェクション対策**
   - 文字列をそのまま連結すると **SOQL インジェクション** のリスクがある。
   - `String.escapeSingleQuotes()` を使ってエスケープする。
   ```apex
   String industry = 'Technology\' OR Name != \'\'';
   String query = 'SELECT Id, Name FROM Account WHERE Industry = \'' + String.escapeSingleQuotes(industry) + '\'';
   ```
   
2. **ガバナ制限**
   - `Database.query()` は **SOQL クエリ制限**（1トランザクション 100 件）にカウントされる。
   - **リスト制限**（1クエリの結果 50,000 件）に注意。

3. **型キャスト**
   - `Database.query()` は `List<SObject>` を返すため、型変換が必要。
   ```apex
   List<Account> accounts = (List<Account>) Database.query(query);
   ```

---

## **まとめ**
- `Database.query()` は **SOQL クエリを動的に生成** できる。
- 検索条件 (`WHERE`)、ソート (`ORDER BY`)、ページネーション (`LIMIT` & `OFFSET`) を柔軟に変更可能。
- `String.escapeSingleQuotes()` を使って **SOQL インジェクション対策** をする。
- **ガバナ制限** に注意しながら適切に設計する。

動的 SOQL を活用すれば、ユーザー入力に基づいた **柔軟な検索機能** を実装できます！ 🚀