CROSS JOIN の使い方
顧客 5 行
- 対応がある組み合わせ
- 10 通り
- 返ってくる組み合わせ(全部)
- 50 通り
緑のマスは「実際に対応がある組み合わせ」です。CROSS JOIN は対応の有無に関係なく、赤いマスも含めて全部返します。
CROSS JOIN は、左のすべての行と右のすべての行を総当たりで組み合わせます。 結果の件数は掛け算です。事故で起きると件数が爆発しますが、「全パターンの枠を先に作る」という正しい使い道もあります。
主要 7 製品で通用 そのまま使える: PostgreSQL / MySQL / MariaDB / SQLite / SQL Server / Oracle Database / IBM Db2
このページの実行結果と図は、すべて本物のデータベースに同じ SQL を流して得たものです。手で書いた「たぶんこうなる」ではありません。
01件数は掛け算になる
結論: `CROSS JOIN` の結果は「左の行数 × 右の行数」です。
条件を書く場所がありません。ON は付けられず、付けようとすると構文エラーになります。総当たりなので絞り込む余地が無い、と考えると分かりやすいはずです。
10 行 × 5 行なら 50 行。小さいうちは平気ですが、1 万行 × 1 万行は 1 億行です。実務の表で不用意に書くと返ってきません。
SELECT j.juchu_id, k.kokyaku_name何を出すかFROM juchu j左(全行)CROSS JOIN kokyaku k右(全行)と総当たりORDER BY j.juchu_id, k.kokyaku_id並び順
ON を書く行がありません。ここが他の結合と決定的に違うところです。
SELECT count(*) AS kensu
FROM juchu
CROSS JOIN kokyaku;| kensu |
|---|
| 50 |
(1 行)
受注 10 行 × 顧客 5 行 = 50 行。掛け算そのものです。
SELECT j.juchu_id, k.kokyaku_name
FROM juchu j
CROSS JOIN kokyaku k
ORDER BY j.juchu_id, k.kokyaku_id;| juchu_id | kokyaku_name |
|---|---|
| 1001 | 青葉工業 |
| 1001 | 白樺物産 |
| 1001 | 黒松システム |
| 1001 | 赤坂商会 |
| 1001 | 三田商事 |
| 1002 | 青葉工業 |
(50 行 … うち先頭 6 行を表示)
02カンマ区切りは CROSS JOIN と同じ
FROM a, b とカンマで並べる古い書き方は、`CROSS JOIN` とまったく同じ意味です。
つまり結合条件を WHERE に書き忘れると、そのまま総当たりになります。「意図しない CROSS JOIN」はこうして生まれます。
新しく書くときは `JOIN ... ON` を使ってください。 結合条件を書く場所が構文として決まっているので、書き忘れに気づけます。
SELECT count(*) AS kanma, (SELECT count(*) FROM juchu CROSS JOIN kokyaku) AS cross_join
FROM juchu, kokyaku;| kanma | cross_join |
|---|---|
| 50 | 50 |
(1 行)
同じ数です。書き方が違うだけで、やっていることは同じです。
03正しい使い道は「全パターンの枠を作る」
総当たりが欲しい場面があります。代表例が集計表の枠づくりです。
「顧客 × 月」のように、データが 1 件も無い組み合わせも 0 として表に出したいとき、先に総当たりの枠を作っておいて、そこへ実績を外部結合します。こうすると「実績が無い月が行ごと抜ける」という問題が起きません。
ふつうに集計しただけでは、実績が無い組み合わせは行そのものが存在しません。グラフや帳票にすると、そこだけ歯抜けになります。枠を先に作るのは、この歯抜けを防ぐためです。
売れなかった商品を 0 件として並べたい、空いている時間枠を見つけたい、といった用途も同じ形です。「無いことを見せたい」ときに CROSS JOIN が要る、と覚えてください。
SELECT k.kokyaku_name, s.shohin_name, count(m.juchu_id) AS kensu
FROM kokyaku k
CROSS JOIN shohin s
LEFT JOIN juchu j ON j.kokyaku_id = k.kokyaku_id AND j.status = 'done'
LEFT JOIN juchu_meisai m ON m.juchu_id = j.juchu_id AND m.shohin_id = s.shohin_id
GROUP BY k.kokyaku_name, s.shohin_name
ORDER BY k.kokyaku_name, s.shohin_name;| kokyaku_name | shohin_name | kensu |
|---|---|---|
| 三田商事 | キーボード | 0 |
| 三田商事 | デスクチェア | 0 |
| 三田商事 | デスクライト | 0 |
| 三田商事 | ノート | 0 |
| 三田商事 | ボールペン | 0 |
| 三田商事 | 付箋 | 0 |
| 三田商事 | 書棚 | 0 |
| 白樺物産 | キーボード | 1 |
(35 行 … うち先頭 8 行を表示)
CROSS JOIN で枠を作り、実績を外部結合で乗せています。0 の行が消えないのがこの書き方の利点です。
04⚠️ よくある間違い:事故なのか意図なのかを、書き方で示す
件数が想定より多いとき、原因はたいてい結合条件の書き忘れです。
そして厄介なことに、カンマ区切りで書いてあると事故か意図かが読み取れません。書いた本人にしか分からない状態になります。
総当たりが意図なら `CROSS JOIN` と明示的に書いてください。 レビューする人が「これはわざとだ」と分かります。逆に CROSS JOIN と書いていないのに総当たりになっていたら、それは事故です。
気づき方: 結果の件数を、元の表の件数と比べてください。掛け算に近い数になっていたら総当たりが起きています。行数が多い表では返ってくる前に気づけないので、**まず count(*) だけ実行して件数を確かめる**のが安全です。
SELECT j.juchu_id, k.kokyaku_name
FROM juchu j, kokyaku k
WHERE j.status = 'done'
ORDER BY j.juchu_id, k.kokyaku_id;| juchu_id | kokyaku_name |
|---|---|
| 1001 | 青葉工業 |
| 1001 | 白樺物産 |
| 1001 | 黒松システム |
| 1001 | 赤坂商会 |
| 1001 | 三田商事 |
| 1002 | 青葉工業 |
(40 行 … うち先頭 6 行を表示)
WHERE があるので一見それらしく見えますが、顧客とつなぐ条件がありません。成立した受注 8 件 × 顧客 5 社で 40 行返っています。
自分で打ってみる
このページの例と同じデータが入った本物のデータベースを、この場(あなたのブラウザの中)で起動できます。打った SQL がサーバーへ送られることはありません。
押すと初回だけ約 9MB を読み込みます(2 回目以降はキャッシュされます)。
解いてみる
読んだだけでは書けるようになりません。実行結果で採点します。
関連するトピック
- INNER JOIN対応する行だけをつなぎたいなら、こちらを使います。
- ON 句と USING 句ON に何を書くか。書き忘れると CROSS JOIN になります。
- LEFT / RIGHT OUTER JOIN作った枠に実績を乗せるときに使います。
根拠(一次情報)
- PostgreSQL 18 マニュアル: 問い合わせ — 結合テーブル(CROSS JOIN)一次情報・確認 2026-07-28
- SQLite: SELECT(join-clause)一次情報・確認 2026-07-28
- Oracle Database SQL Language Reference: SELECT(cross join)一次情報・確認 2026-07-28