SOQL バインド変数について


SalesforceのSOQL（Salesforce Object Query Language）の **バインド（Bind Variables）** は、Apexコード内で変数を使ってクエリを動的に構築する方法です。  
これは、SQLにおける **プレースホルダー（?）** と同様の役割を果たし、セキュリティやパフォーマンスの向上に役立ちます。

---

## 🔹 **SOQLのバインド変数とは？**
バインド変数を使うと、Apexの変数をSOQLクエリの条件（`WHERE`句など）に埋め込むことができます。  
バインド変数の記述には、変数の前にコロン（`:`）を付けます。

**✅ 例：バインド変数を使ったSOQL**
```apex
String accountName = 'Acme Corporation';
List<Account> accounts = [SELECT Id, Name FROM Account WHERE Name = :accountName];
```
上記のコードでは、`accountName` 変数の値 **'Acme Corporation'** をSOQLの`WHERE`句にバインドしています。

---

## 🔹 **バインド変数を使うメリット**
1. **SQLインジェクションを防ぐ**
   - 文字列の連結でクエリを構築すると、悪意のあるデータが注入されるリスクがある。
   - バインド変数を使うと、Salesforceが自動的に値をエスケープ処理するため安全。

   **❌ 悪い例（SQLインジェクションのリスクあり）**
   ```apex
   String accountName = 'Acme Corporation';
   List<Account> accounts = Database.query('SELECT Id, Name FROM Account WHERE Name = \'' + accountName + '\'');
   ```
   これは、外部から`accountName`に`' OR Name != ''` のような文字列が渡されると、不正なクエリが実行される可能性がある。

2. **クエリパフォーマンスの向上**
   - バインド変数を使うと、SalesforceのSOQLエンジンが**クエリ計画（Query Plan）をキャッシュ**しやすくなる。
   - 固定値を直接書くよりも、最適化されたクエリ実行が可能になる。

3. **コードの可読性・再利用性が向上**
   - 変数を使うことで、クエリを柔軟にカスタマイズできる。

---

## 🔹 **バインドできるデータ型**
以下のデータ型の変数をバインドできます。

| データ型         | 使用例 |
|-----------------|-------------------------------------------|
| `String`       | `:accountName` |
| `Integer`      | `:recordCount` |
| `Decimal`      | `:totalAmount` |
| `Boolean`      | `:isActive` |
| `Date`         | `:today` |
| `DateTime`     | `:currentTime` |
| `Id`           | `:recordId` |
| `Set<T>` / `List<T>` | `:accountIds`（IN句で利用） |

---

## 🔹 **バインド変数の具体例**
### ① **文字列 (`String`) のバインド**
```apex
String targetName = 'Tech Corp';
List<Account> accounts = [SELECT Id, Name FROM Account WHERE Name = :targetName];
```
👉 `targetName` の値が `Tech Corp` なら、その会社のレコードを取得。

---

### ② **数値 (`Integer`) のバインド**
```apex
Integer recordCount = 5;
List<Contact> contacts = [SELECT Id, FirstName, LastName FROM Contact LIMIT :recordCount];
```
👉 `LIMIT` 句に変数をバインドし、5件だけ取得。

---

### ③ **日付 (`Date`) のバインド**
```apex
Date today = Date.today();
List<Opportunity> opps = [SELECT Id, Name FROM Opportunity WHERE CloseDate = :today];
```
👉 今日の `CloseDate` の商談を取得。

---

### ④ **リスト (`List<T>` または `Set<T>`) のバインド**
複数のIDを検索条件に使う場合、`IN` 句でバインドできる。

```apex
List<Id> accountIds = new List<Id>{'001XXXXXXXXXXXX', '001YYYYYYYYYYYY'};
List<Contact> contacts = [SELECT Id, FirstName FROM Contact WHERE AccountId IN :accountIds];
```
👉 `accountIds` に含まれる `AccountId` を持つ `Contact` を取得。

---

### ⑤ **オブジェクト (`SObject`) のバインド**
SOQLでは`SObject`全体をバインドできる。  
ただし、**バインドできるのはそのオブジェクトのIDのみ**。

```apex
Account acc = [SELECT Id FROM Account WHERE Name = 'Tech Corp' LIMIT 1];
List<Contact> contacts = [SELECT Id, FirstName FROM Contact WHERE AccountId = :acc];
```
👉 `acc` は `Account` オブジェクトだが、バインドされるのは `acc.Id`。

---

## 🔹 **バインド変数の制限**
1. **WHERE句の右側でのみ使用可能**
   - バインド変数は **`WHERE` や `LIMIT` 句の右側** でのみ使用可能。
   - `SELECT` や `ORDER BY` では使用できない。

   **❌ NG例**
   ```apex
   String fieldName = 'Name';
   List<Account> accounts = [SELECT :fieldName FROM Account]; // エラー！
   ```

2. **バインド変数のデータ型に注意**
   - `Set<T>` や `List<T>` を `IN` 句で使う場合、型が一致している必要がある。
   - `Date` と `DateTime` の違いに注意（`DateTime` には `GMT` が含まれる）。

---

## 🔹 **動的SOQL（Database.query）とバインド**
`Database.query()` を使う場合も、バインド変数を使うことが推奨される。

```apex
String targetName = 'Tech Corp';
String query = 'SELECT Id, Name FROM Account WHERE Name = :targetName';
List<Account> accounts = Database.query(query);
```
👉 `Database.query()` でもバインド変数を利用可能。  
👉 直接 `+` で文字列連結するとSQLインジェクションのリスクがあるため避ける。

---

## 🔹 **まとめ**
| 項目 | 説明 |
|------|------|
| **バインド変数とは？** | Apex変数をSOQLクエリに埋め込む機能 |
| **メリット** | SQLインジェクション防止、パフォーマンス向上、可読性向上 |
| **バインド可能な型** | `String`, `Integer`, `Date`, `Id`, `List<T>` など |
| **IN句でリストバインド可能？** | 可能 (`List<T>` や `Set<T>` を使用) |
| **動的SOQLでバインド可能？** | 可能 (`Database.query()` で `:` を使用) |

バインド変数を適切に使うことで、**安全でパフォーマンスの高いSOQL** を書くことができます！🚀