基本的な構文は以下のとおりです:
=QUERY('P&L'!A:F, "SELECT A, B, C, D ORDER BY D DESC", 1)
3番目の引数(1)はヘッダー行の数です。列のアルファベット(A、B、D)は、範囲内の位置ではなく、ソースタブの実際のスプレッドシート列を指します。
なぜORDER BYは手動ソートより優れているのか
「データ → 範囲を並べ替え」でソースタブを並べ替えると、行が物理的に移動します。別のタブから'P&L'!C14を参照している数式は、突然まったく別の行を指してしまいます。P&L・貸借対照表・キャッシュフロー・FCFF・前提条件・リターン・感応度分析・アウトプットといった8タブ構成のモデルでは、不用意なソート一つが数十箇所の参照崩れを連鎖的に引き起こします。
QUERY ORDER BYは、ソートされた配列を別の表示専用セル範囲に書き出します。ソースデータはそのまま保持されます。経営報告資料をCFOに渡した後、担当者が直感的にソートしたくなっても、上流データが動く心配はありません。
構文:昇順、降順、複数列ソート
デフォルトは昇順です。財務の場面では、金額の大きい順(降順)にしたいケースがほとんどです:
=QUERY('Revenue'!A:E, "SELECT A, B, E ORDER BY E DESC", 1)
複数列ソートはSQLと同じ書き方で、キーをカンマでつなぎます:
=QUERY('Pipeline'!A:G,
"SELECT A, B, C, D ORDER BY C DESC, D ASC", 1)
ARR降順でソートし、同額の場合は成約予定日の昇順でタイブレークする設定です。40件超の案件を抱えるM&Aや大型投資のパイプライン管理では、案件規模で優先順位をつけながら、同規模案件は時系列で整理できます。
複数タブのQUERY:列参照の落とし穴
1つのタブから連続した範囲を参照する場合、列アルファベットが使えます:
=QUERY('FCFF'!A:H, "SELECT A, B, G, H ORDER BY G DESC", 1)
ただし、連続していない列や複数タブから配列リテラルを組み立てた場合、QUERYは元の列アルファベットを追跡できなくなります。そのため、Col1・Col2という位置指定の書き方に切り替える必要があります:
// これは失敗します
=QUERY({'P&L'!C:C, 'P&L'!F:F}, "SELECT C, F ORDER BY F DESC", 1)
// こちらが正しい書き方です
=QUERY({'P&L'!C:C, 'P&L'!F:F}, "SELECT Col1, Col2 ORDER BY Col2 DESC", 1)
2026年6月現在、この挙動の違いはGoogleのVisualization API Query Language リファレンスに記載されていますが、かなり奥まった場所にあります。同ドキュメントによると「列はカラムIDで参照され、Sheetsの直接範囲ではスプレッドシートの列アルファベットが使われますが、配列リテラルでは位置識別子になります」とのことです。また、GoogleのGoogle Sheetsの数式の概要でも、QUERY関数が他の配列生成関数と組み合わさった場合の参照挙動について補足説明があります。
複数ソースのサマリービューを構築するとき、多くのアナリストが一度はこのエラーに遭遇します。
複数タブの行を縦に積む(四半期連結の定番パターン)場合:
=QUERY({'Jan'!A:E; 'Feb'!A:E; 'Mar'!A:E},
"SELECT Col1, Col2, Col5 ORDER BY Col5 DESC", 1)
セミコロンで行を縦積み、カンマで列を横積みです。
WHERE + ORDER BY の組み合わせ
多くの場面でフィルターとソートを同時に使いたくなります。先にフィルターし、その結果をソートする形です:
=QUERY('SKU Data'!A:F,
"SELECT A, B, D, F WHERE F > 0.35 ORDER BY F DESC",
1)
貢献利益率(F列)が35%を超えるSKUだけを抽出し、降順に並べます。取締役会資料のSKU別貢献利益率スライドなら、この1つの数式で完結します。補助列も手動フィルターも不要です。
前提条件タブの日付セルを動的に参照する場合:
=QUERY('Transactions'!A:E,
"SELECT A, B, C, E WHERE B >= date '"&TEXT(Assumptions!$B$3,"yyyy-mm-dd")&"' ORDER BY E DESC",
1)
TEXT()でラップすることで、前提条件セルをQUERYが期待するyyyy-mm-dd形式に変換します。これを省略すると、データ型の違いによってすべての行が通過するか、まったく通過しないかのどちらかになります。エラーは表示されず、ただ間違ったデータが返る--モデルにおいて最も厄介なバグです。
表示しない列でソートする
QUERY ORDER BYでは、出力に含めない列を基準にソートできます。SORT()ではこれがうまくできません。
=QUERY('Headcount'!A:G,
"SELECT A, B, C ORDER BY G DESC",
1)
出力には氏名・部門・給与バンド(A〜C列)だけが表示されます。入社日(G列)で降順にソートしていますが、G列はアウトプットに現れません。経営報告向けの人員一覧で「最近の入社者を上位に表示したいが、日付欄は見せたくない」という場面に最適なパターンです。
採用計画の感応度分析:実践的な例
採用ペースとキャッシュバーンの感応度分析を行う場合、計画中の採用者を月次コストの高い順に並べておくと便利です。ランウェイ(資金余命)が18ヶ月を下回りそうな局面で、CFOがリストの上から順に削減候補を確認できます:
=QUERY('Headcount'!A:J,
"SELECT A, C, D, J WHERE D = 'Planned' ORDER BY J DESC",
1)
J列は月次の総人件費(フルロードコスト)です。ARR4.2億円・月次バーン3,800万円の企業において、エンジニア3名の採用計画が月次コスト520万円の増加に相当することを把握し、その優先順位が一目でわかる--これが実際の意思決定につながる分析です。ソートされた出力がライブの人員タブから生成されるため、CFOは常に最新データを参照できます。古いスナップショットを心配する必要はありません。
QUERY ORDER BY と SORT() の比較
QUERY ORDER BY | SORT() | |
|---|---|---|
| ソートしながらフィルター | 可能(WHERE) | FILTERのネストが必要 |
| 複数列ソート | 可能 | 可能 |
| 列の選択表示 | 可能(SELECT) | 不可 |
| ヘッダーの扱い | 第3引数で指定 | 手動で除外が必要 |
| 配列リテラルのソース | Col1・Col2構文が必要 | よりシンプル |
| 集計処理 | 可能(GROUP BY・SUM) | 不可 |
「連続した範囲を特定の列で並べ替えるだけ」ならSORT()のほうが短く書けます。しかし、フィルター・列選択・ソートを同時に行う場面--経営報告資料の作成ではほとんどがこのケースです--QUERYなら1つの数式で完結します。
よくあるエラーのFAQ
Q. ソートが意図通りにならないのはなぜですか?
数値がテキストとして保存されている可能性があります。QUERYはテキストを辞書順に並べるため、「100」が「20」より前に来てしまいます。ソース列をVALUE()でラップするか、セルの配置を確認してください(本物の数値は右寄せ、テキスト形式の数値は左寄せになります)。
Q. 配列リテラルの範囲で#VALUE!エラーが出ます。
{}内の配列リテラルで列アルファベット(A、Bなど)を使っています。Col1・Col2の位置指定に切り替えてください。上述の「複数タブのQUERY:列参照の落とし穴」セクションも参照してください。
Q. 日付フィルターを設定したのに、すべての行が通過する(またはまったく通過しない)のはなぜですか?
日付セル参照をTEXT(セル, "yyyy-mm-dd")でラップしていない可能性があります。QUERYが期待する日付フォーマットと合っていないと、データ型の不一致により意図しない動作が起きます。エラーは表示されず、ただ間違ったデータが返るため注意が必要です。
Q. ORDER BYが完全に無視されます。
GROUP BYやPIVOTの後にORDER BYを記述していないか確認してください。特定の組み合わせではこれらの句が競合します。まずグループ化を完結させ、その出力を2つ目のQUERYでラップしてソートしてください。
こうしたソートビューをより大きなモデルの一部として構築する場合、ソートのロジックを言葉で説明するだけでQUERY数式を生成・更新してくれるツールがあります。「粗利益率35%超のSKUを売上高の降順で表示して」と入力するだけで、ModelMonkeyがサイドバーから数式を作成します。
ModelMonkeyのプランを選ぶ--Google SheetsとExcelの両方に対応しています。