mcpField
S
sf-soql

sf-soql

github.com/clientell-aiデータ・データベース20104 回インストール32 週間前に更新
リポジトリ

sf-soql について

Salesforce 開発者向けの SOQL クエリ構築・最適化ガイドです。リレーション、集計、SOSL、動的 SOQL を扱い、ユーザーモードのセキュリティ、バインド変数、選択的な条件、各種制限を重視します。

主な機能

  1. 01リレーション・集計クエリの構築
  2. 02全文検索に SOSL を使用
  3. 03ユーザーモードとバインド変数の適用
  4. 04非効率なクエリパターンの特定
  5. 05クエリプランによる性能分析
  6. 06クエリ制限と構文上の注意点を確認

活用シーン

  1. 01アクセス制御に沿った Salesforce クエリの作成
  2. 02複数オブジェクトを横断したテキスト検索
  3. 03大規模データ向けクエリの最適化
  4. 04クエリプランや制限の問題を調査

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/clientell-ai/salesforce-skills --skill sf-soql

インストール後、Claude Code で/sf-soqlを入力して読み込まれたか確認します。

同梱ファイルreferences/soql-reference.md

詳細

SOQL クエリビルダーとオプティマイザー

Salesforce SOQL のスペシャリストとして、最適化された安全なクエリを作成します。

セキュリティを最優先

  • CRUD/FLS を適用するため、必ず WITH USER_MODE を使用します
  • 動的 SOQL で文字列連結を使用してはいけません。バインド変数を使用します
  • Database.query() は、動的クエリが本当に必要な場合にのみ使用します
// GOOD
List<Account> accounts = [
    SELECT Id, Name, Industry
    FROM Account
    WHERE Industry = :industryFilter
    WITH USER_MODE
    LIMIT 200
];

// BAD — injection risk
String query = 'SELECT Id FROM Account WHERE Name = \'' + userInput + '\'';

クエリパターン

親から子へのクエリ(サブクエリ)
SELECT Id, Name,
    (SELECT Id, FirstName, LastName FROM Contacts)
FROM Account
WHERE Industry = 'Technology'
WITH USER_MODE
子から親へのクエリ(ドット表記)
SELECT Id, FirstName, Account.Name, Account.Industry
FROM Contact
WHERE Account.Industry = 'Technology'
WITH USER_MODE
集計クエリ
SELECT Industry, COUNT(Id) cnt, SUM(AnnualRevenue) totalRevenue
FROM Account
WHERE Industry != null
WITH USER_MODE
GROUP BY Industry
HAVING COUNT(Id) > 5
ORDER BY COUNT(Id) DESC
多態型(TYPEOF)
SELECT Id, Subject,
    TYPEOF What
        WHEN Account THEN Name, Industry
        WHEN Opportunity THEN Name, StageName, Amount
    END
FROM Task
WITH USER_MODE
セミジョインとアンチジョイン
-- Semi-join: Accounts WITH contacts
SELECT Id, Name FROM Account
WHERE Id IN (SELECT AccountId FROM Contact)
WITH USER_MODE

-- Anti-join: Accounts WITHOUT opportunities
SELECT Id, Name FROM Account
WHERE Id NOT IN (SELECT AccountId FROM Opportunity)
WITH USER_MODE
SOSL(検索言語)

複数のオブジェクトを横断する全文検索には、SOSL を使用します。

FIND {SearchTerm} IN ALL FIELDS
RETURNING Account(Id, Name WHERE Industry = 'Tech'),
          Contact(Id, FirstName, LastName)
LIMIT 20
  • SOSL を使用する場合:オブジェクトを横断したテキスト検索、あいまい検索、部分一致検索
  • SOQL を使用する場合:完全一致、リレーションクエリ、集計、DML 関連のクエリ
  • ガバナ制限:1 トランザクションあたり 20 件の SOSL クエリ
日付リテラル
リテラル意味
TODAY, YESTERDAY, TOMORROW暦日
THIS_WEEK, LAST_WEEK, NEXT_WEEK日曜日から土曜日までの週
THIS_MONTH, LAST_MONTH, NEXT_MONTH暦月
THIS_QUARTER, LAST_QUARTER暦四半期
THIS_YEAR, LAST_YEAR, NEXT_YEAR暦年
LAST_N_DAYS:n過去 n 日間(今日を含む)
NEXT_N_DAYS:n今後 n 日間(今日を含む)
LAST_90_DAYS過去 90 日間
THIS_FISCAL_QUARTER, THIS_FISCAL_YEAR会計期間
N_DAYS_AGO:nちょうど n 日前
FIELDS() 関数
SELECT FIELDS(ALL) FROM Account LIMIT 200    -- All fields (LIMIT required)
SELECT FIELDS(STANDARD) FROM Account          -- Standard fields only
SELECT FIELDS(CUSTOM) FROM Account            -- Custom fields only
動的 SOQL
String query = 'SELECT Id, Name FROM Account WHERE Industry = :industry';
List<Account> results = Database.query(query, AccessLevel.USER_MODE);
  • Database.query() では常に AccessLevel.USER_MODE を使用します
  • :bindVariable 構文を使用し、文字列連結は決して使用しません
  • フィールド名が本当に動的な場合:String.escapeSingleQuotes()
ユーティリティ関数
  • toLabel(PicklistField) — 翻訳済みの選択リストラベルを返します
  • FORMAT(NumberField) — ロケールに応じて数値/日付をフォーマットします
  • convertCurrency(Amount) — ユーザーの通貨に換算します(複数通貨対応組織)
レコードロック
SELECT Id, Name FROM Account WHERE Id = :accountId FOR UPDATE

悲観的ロックにより、コミットまたはロールバックされるまで、ほかのトランザクションによる更新がブロックされます。

ALL ROWS(削除済みレコードを含む)
SELECT Id, Name FROM Account WHERE IsDeleted = true ALL ROWS

論理削除されたレコードを返します(ごみ箱に 15 日間保持されます)。

SOQL For ループ
for (List<Account> batch : [SELECT Id, Name FROM Account]) {
    // Processes 200 records per iteration automatically
    // Uses minimal heap — ideal for large datasets
}
地理位置情報クエリ
SELECT Id, Name, DISTANCE(Location__c, GEOLOCATION(37.7749, -122.4194), 'mi') dist
FROM Store__c
WHERE DISTANCE(Location__c, GEOLOCATION(37.7749, -122.4194), 'mi') < 50
ORDER BY DISTANCE(Location__c, GEOLOCATION(37.7749, -122.4194), 'mi')
WITH SECURITY_ENFORCED と WITH USER_MODE の比較
機能SECURITY_ENFORCEDUSER_MODE
FLS の適用SELECT/FROM のみSELECT/FROM/WHERE/サブクエリ
違反時の動作例外をスローアクセスできないフィールドを黙って除外
制限ルール非対応対応
推奨レガシー — 移行してください推奨

最適化ルール

選択性の高いフィルター(インデックス付きフィールドを使用)
  • Id、Name、OwnerId、CreatedDate、SystemModstamp
  • RecordTypeId、Lookup フィールド、External ID フィールド
  • External ID としてマークされたカスタムフィールド、またはカスタムインデックス付きフィールド
検出するアンチパターン
  1. ループ内のクエリ — クエリをループの前に移動し、Map/Set を使用します
  2. 選択性の低いフィルター — まずインデックス付きフィールドで絞り込みます
  3. SELECT * 相当 — すべてのフィールドではなく、必要なフィールドのみを選択します
  4. LIMIT の不足 — 大量のデータセットを返す可能性のあるクエリには LIMIT を追加します
  5. 否定的なフィルター — != と NOT IN は選択性が低くなります
  6. 先頭のワイルドカード — LIKE '%term' ではインデックスを使用できません
  7. WHERE 句の不足 — 意図的にすべてを読み込む場合を除き、必ず絞り込みます
クエリプランの分析
sf data query -q "EXPLAIN SELECT Id FROM Account WHERE Name = 'Test'" --target-org myOrg

制限

  • 同期トランザクションあたり 100 件の SOQL クエリ
  • 非同期トランザクションあたり 200 件の SOQL クエリ
  • トランザクションあたり 50,000 件の返却行
  • サブクエリあたり 2,000 件の行
  • 親クエリあたり 20 件のリレーションクエリ

注意点

  • FIELDS(ALL) には LIMIT 200 が必須です。指定しないと失敗します
  • COUNT() は null を含むすべての行をカウントします。COUNT(fieldName) は null 以外のみをカウントします
  • FOR UPDATE は行全体をロックします。他のトランザクションは待機するか、タイムアウトします
  • SOSL には独自のガバナー制限があり、1 トランザクションあたり 20 クエリです(SOQL の 100 とは別です)
  • 日付リテラルには境界日が含まれます。LAST_N_DAYS:7 には今日が含まれます
  • TYPEOF は Task.What、Event.Who などの多態性フィールドでのみ機能します
  • サブクエリの結果上限は 2,000 行であり、50,000 行ではありません
  • FIELDS(ALL) は Apex ではサポートされていません。REST API と Developer Console でのみ使用できます
  • Database.query() は FIELDS() をサポートしていません。明示的なフィールドリストを使用してください
  • ALL ROWS は FOR UPDATE と併用できません

ワークフロー

  1. データ要件を把握します
  2. オブジェクトのリレーションとフィールドの型を確認します
  3. 適切なフィルター、セキュリティ、制限を設定してクエリを作成します
  4. 次のコマンドでテストします:sf data query -q "YOUR_QUERY" --target-org myOrg
  5. 結果と説明プランに基づいて最適化します

参考資料

  • SOQL/SOSL リファレンス — 日付リテラル、SOSL 構文、FIELDS()、地理位置情報、動的 SOQL、集計、FOR UPDATE、バインドパターン、クエリプラン
  • ガバナー制限 — トランザクションあたりの SOQL クエリ制限

コメント