【図解】SOQL の書き方入門|SQL との違い、親子のリレーション、日付リテラル、101エラーまで

このページには広告リンクを含みます。

SOQL の基本の書き方

SOQL の土台は SELECT と FROM の2つの句です。WHERE、ORDER BY、LIMIT は必要に応じて足します。

SELECT Id, Name, Industry
FROM Account
WHERE Industry = 'Media'
ORDER BY Name
LIMIT 10

書くときに最初につまずくのが項目名です。SOQL に書くのは「取引先名」のような表示ラベルではなく、Name のような API 参照名です。カスタム項目やカスタムオブジェクトの名前は __c で終わります。

書き順の感覚は、レポートと似ています。どのオブジェクトの、どの項目を、どの条件で見るかを決めるだけです。Trailhead でも SOQL の作成をレポートの作成になぞらえて説明しています。

SOQL は取得のための言語です。レコードの作成や更新は DML の仕事で、SOQL ではできません。

SOQL と SQL の違い

SQL を少し知っている人が迷う所を表にしました。

SQL では JOIN で2つの表をつなぐのに対し、SOQL では定義済みのリレーションをドットとサブクエリでたどることを左右に比べた図
SQL は書く人が結合の条件を決める。SOQL は定義済みのリレーションをたどる
やりたいこと SQL SOQL
全項目を取る SELECT * ない。項目を列挙するか FIELDS()
表をつなぐ JOIN ... ON リレーションクエリ。任意の結合はできない
列の計算 Amount * 2 のような式 項目リストで計算式は使えない
名前 列名 API 参照名
文字列の引用符 データベースによる 一重引用符のみ。二重はエラー
日付 文字列として書くことが多い 引用符なし。日付リテラルが使える
更新 UPDATE 文 できない。DML を使う

SELECT * の代わりに FIELDS() を使う

API 51.0 以降は FIELDS() で項目の組を指定できます。項目名が分からないオブジェクトを調べるときに便利です。

SELECT FIELDS(ALL) FROM Account LIMIT 200
使う場所 FIELDS(STANDARD) FIELDS(ALL)、FIELDS(CUSTOM)
Apex 使える 使えない
Bulk API 2.0 使える 使えない
Salesforce CLI 使える 行数を制限すれば使える
REST API、SOAP API 使える 行数を制限すれば使える

行数の制限は LIMIT 200 以下か、200件以内の ID 指定です。

SELECT Id, FIELDS(ALL) のように重複すると duplicate field selected: Id というエラーになります。項目レベルセキュリティは守られるので、見る権限のない項目は返りません。調査には便利ですが、Apex では使えないため、コードには必要な項目だけを書きます。

親子のリレーションの書き方

JOIN の代わりになるのがリレーションクエリです。向きによって書き方が変わります。

取引先責任者から取引先へはドットでたどり、取引先から取引先責任者へはかっこのサブクエリでたどることを上下の矢印で示した図
子から親はドット、親から子はサブクエリ。向きで書き方が変わる

子から親へ:ドットでたどる

取引先責任者(Contact)から親の取引先(Account)の項目を取ります。WHERE でも同じようにたどれます。

SELECT Id, Name, Account.Name
FROM Contact
WHERE Account.Industry = 'Media'

親から子へ:サブクエリを書く

取引先から、ぶら下がる取引先責任者を取ります。FROM に書くのは子リレーション名で、標準オブジェクトでは子オブジェクト名の複数形です。Contact ではなく Contacts と書く点に注意します。

SELECT Name,
  (SELECT LastName FROM Contacts)
FROM Account
WHERE Industry = 'Media'

サブクエリの中に WHERE も書けます。冒頭の依頼なら、次の形になります。

SELECT Name,
  (SELECT Name, Amount, CloseDate
   FROM Opportunities
   WHERE IsClosed = FALSE)
FROM Account

カスタムのリレーションは __r

カスタムの参照関係や主従関係では、__c を __r に換えます。オブジェクトと項目の名前は説明のための例です。

子から親へたどるときは、リレーション項目の名前の __c を __r にします。

SELECT Id, Region__r.Name FROM Shop__c

親から子へたどるときは、子リレーション名に __r を付けます。

SELECT Name, (SELECT Name FROM Shops__r) FROM Region__c

開発者コンソールで試すときは、サンプルをまとめて貼らず、1文ずつ実行します。

子から親で使う名前はリレーション項目の名前、親から子で使う名前は子リレーション名です。子リレーション名は項目を作るときに決めるので、迷ったらオブジェクトマネージャーのリレーション項目で確かめます。オブジェクト同士の関係は主従関係と参照関係の違いで図解しています。

リレーションクエリの上限 値
子から親のリレーション 1クエリ55個まで
親から子のリレーション 1クエリ20個まで
子から親の深さ 5階層まで
親から子の深さ 5階層まで(API 58.0 以降の REST、SOAP、Apex。親を1階層目と数える)

親から子の5階層は、Bulk API、Bulk API 2.0、ビッグオブジェクト、外部オブジェクトでは使えません。API 57.0 以前は2階層までです。

WHERE の書き方と日付リテラル

WHERE の値には書き方の決まりがあります。

  • 文字列は一重引用符。二重引用符はエラー
  • 日付と数値は引用符なし
  • チェックボックスは TRUE、FALSE
  • 項目名は引用符で囲まない
  • 値に別の項目名や計算は書けない(2項目の足し引きで絞る FORMULA() はベータ版で、WHERE 専用)
  • AND と OR を混ぜるときは、かっこで順序を決める
SELECT Name, Email
FROM Contact
WHERE LastName IN ('James', 'Barr')
  AND (MailingState = 'Tokyo' OR Title LIKE '%部長')

IN は値の並びとの比較、LIKE は % を使ったあいまい一致です。

日付リテラルは「範囲」を表す

日付の条件は、固定の日付でも、相対の日付リテラルでも書けます。項目の型で形式が変わります。

date 型の項目には日付だけを書きます。

SELECT Id FROM Opportunity WHERE CloseDate = 2026-10-31

dateTime 型の項目には日時を書きます。日本時間はオフセットで指定します。

SELECT Id FROM Account WHERE CreatedDate > 2026-10-01T00:00:00+09:00

日付リテラルは、そのまま書きます。

SELECT Id FROM Opportunity WHERE CloseDate = THIS_QUARTER

日付リテラルは1時点ではなく期間です。= は期間の中、> と < は期間の外側という意味になります。たとえば CloseDate > THIS_MONTH は「今月より後」です。

日付リテラル 表す範囲 例
TODAY 今日 CreatedDate = TODAY
YESTERDAY 昨日 CreatedDate = YESTERDAY
THIS_WEEK 今週 CloseDate = THIS_WEEK
THIS_MONTH 今月 CloseDate = THIS_MONTH
LAST_MONTH 先月 CreatedDate = LAST_MONTH
THIS_QUARTER 今の暦の四半期 CloseDate = THIS_QUARTER
THIS_YEAR 今年 CloseDate = THIS_YEAR
LAST_90_DAYS 90日前から現在まで(今日を含む) CreatedDate = LAST_90_DAYS
LAST_N_DAYS:n n日前から現在まで(今日を含む) CreatedDate = LAST_N_DAYS:30
NEXT_N_DAYS:n 明日からn日間(今日を含まない) CloseDate = NEXT_N_DAYS:15

ハマりやすい点が3つあります。

  • THIS_QUARTER は暦の四半期です。設定の会計年度に合わせるなら THIS_FISCAL_QUARTER を使います。公式は FISCAL 系のリテラルの前提として、カスタム会計年度の定義を挙げています。
  • 週の始まりなどの範囲は、クエリを実行するユーザーのロケールで変わります。ユーザーに設定がなければ組織のロケールです。
  • dateTime は UTC で保存されています。Z を付けて書くと UTC として解釈されるので、日本時間で見たいなら +09:00 のようにオフセットを付けます。

SOQL を実行する場所

目的に応じて選びます。最初は開発者コンソールで十分です。

場所 向いている用途 書き方の要点
開発者コンソール 手で試す、結果を見る クエリエディターに貼って実行
Salesforce CLI 結果を CSV や JSON で保存する sf data query
VS Code の拡張 プロジェクトの中で書く 拡張の SOQL 機能を使う
Apex 処理の中で使う 角かっこで囲む
REST API 外部から取得する q パラメータに渡す

開発者コンソール

Trailhead の手順では、設定のクイック検索で Web Console を開き、Enable Developer Console を有効にしてページを更新してから、開発者コンソールを起動します。下のパネルの Query Editor タブにクエリを書いて Execute を押します。

File メニューからオブジェクトを開き、項目を選んで Query を押すと、API 参照名の入ったクエリが自動で作られます。名前が分からないときの近道です。

Salesforce CLI

--target-org には接続済みの組織の別名を指定します。CLI の準備はSalesforce CLI の始め方で説明しています。

sf data query --query "SELECT Id, Name FROM Account LIMIT 10" --target-org my-dev
sf data query --file query.txt --result-format csv --output-file output.csv --target-org my-dev

10,000件を超えそうなときは、公式の説明どおり sf data export bulk を使います。

Apex

Apex では角かっこで囲み、変数はコロンを付けて渡します。

String industry = 'Media';
List<Account> accounts = [
    SELECT Id, Name FROM Account WHERE Industry = :industry
];

試す組織がなければDeveloper Edition の作り方を先に見てください。

よくあるエラーと直し方

エラーや症状 原因 直し方
Too many SOQL queries: 101 1トランザクションで SOQL が100回を超えた ループの外で1回にまとめる
Too many query rows: 50001 取得行の合計が50,000行を超えた WHERE で対象を絞る、処理を分ける
Non-selective query against large object type 大きなオブジェクトで、選択的な絞り込みがない 索引のある項目で絞る、Query Plan で調べる
duplicate field selected: Id 項目リストの重複 FIELDS() と個別の項目が重ならないようにする
QUERY_TOO_COMPLICATED 文が長く複雑(数式項目が多いなど) 取る項目を減らす
文字列の引用符のエラー 二重引用符を使った 一重引用符に直す
CLI で件数が多い sf data query は既定の API を使う sf data export bulk に換える

ガバナ制限のうち、SOQL に関わる値は次のとおりです。

上限 同期 非同期
SOQL の回数 100 200
SOQL で取得する合計行 50,000 50,000
親子のサブクエリの数 トップレベルの回数の上限の3倍 同左

非同期はバッチ Apex や future メソッドの値で、スケジュール Apex には同期の上限が適用されます。サブクエリを含む SOQL は、親子リレーションごとに追加のクエリとして数えられます。ガバナ制限の例外は捕捉できず、処理が止まります。

101 エラーの直し方

ヘルプの例は、取引先のトリガの for ループの中で取引先責任者を取るコードです。取引先が101件まとめて処理されると、101回目で止まります。

ループの中で1件ごとにSOQLを実行して101回目に止まる書き方と、先にIDを集めてループの外でINを使い1回で取る書き方を上下に比べた図
先に ID を集めて IN で1回で取れば、101件でも SOQL は1回で済む
// やってはいけない形:1件ごとに SOQL を使う
for (Account a : Trigger.new) {
    List<Contact> cons = [SELECT Id FROM Contact WHERE AccountId = :a.Id];
}

// 直した形:ID を集めて、ループの外で1回だけ
Set<Id> accountIds = new Set<Id>();
for (Account a : Trigger.new) {
    accountIds.add(a.Id);
}
List<Contact> cons = [
    SELECT Id, AccountId FROM Contact WHERE AccountId IN :accountIds
];

この上限は、同じトランザクションのトリガ、クラス、フローの分も合算されます。ヘルプによれば引き上げもできません。フローで同じことが起きる場合の考え方はフローのループ内の更新を直す記事に書きました。同じトランザクションで何が動くかは保存時の実行順序で確かめられます。

SOQL が向かない場面

レポートと SOQL は、どちらかが上位というものではありません。

  • 営業の担当者が自分で条件を変えて見るなら、レポートとダッシュボードが向きます。
  • どのオブジェクトにあるか分からない語を探すなら、SOQL ではなく SOSL を使います。公式は、対象のオブジェクトや項目が分からないときの選択肢として SOSL を挙げています。
  • レコードを大量に更新するなら、データ操作の道具を使います。データローダの使い方を見てください。

関連記事

よくある質問

SOQL と SQL の違いは何ですか?
SOQL は Salesforce のデータを取得するための言語で、書き方は SQL の SELECT に似ています。ただし SELECT * のような項目のワイルドカード、任意の JOIN、計算式は使えず、オブジェクト間はリレーションクエリでたどります。
SOQL で SELECT * の代わりに全項目を取るには?
FIELDS(ALL) を使います。API 51.0 以降で使え、CLI や API では LIMIT 200 以下などで行数を制限する必要があります。Apex では FIELDS(ALL) と FIELDS(CUSTOM) は使えないので、項目を列挙します。
SOQL で親オブジェクトの項目を取るにはどう書きますか?
子から親へはドットでたどります。取引先責任者から取引先名を取るなら SELECT Id, Account.Name FROM Contact です。カスタムのリレーションは項目名の __c を __r に換えます。
SOQL の日付の条件に引用符は必要ですか?
不要です。SOQL の日付と数値には引用符を付けません。WHERE CreatedDate = THIS_MONTH のように日付リテラルをそのまま書くか、date 型の項目なら 2026-10-01、dateTime 型の項目なら 2026-10-01T00:00:00+09:00 の形で書きます。
Too many SOQL queries: 101 はどう直しますか?
1トランザクションで SOQL が100回を超えたときのエラーです。ループの中の SOQL をやめ、先に ID を集めてからループの外で IN を使った1回のクエリにまとめます。