基本情報のSQLで結合後の行数を数える|重複キーとNULLの練習

執筆・編集:まっく部長公開日:

JOINの行数は、条件に一致する行の組の数です。左のAが2行、右のAも2行なら2×2=4組になります。通常の等号ではNULL同士も一致しないため、値が同じに見える行をまとめずに数えます。

この例は左が5行、右が4行です。INNER JOINでは一致した5組、LEFT JOINでは相手のいない左の2行も加えて7行になります。行IDの組で追うと、入力行数・結合後の行数・人数を区別できます。

この練習の条件
  • 等号による結合だけを扱う。照合するgrp列には重複とNULLがある
  • 各表のidは一意でNULLにならない。grpは主キーではない
  • NULLは文字列の「NULL」や空文字ではない。表では値がない位置をNULLと表記する
  • WHERE・複数表の連鎖・製品ごとの型変換や照合順序は、この練習の範囲に含めない
目次

同じAが左右2行ずつあれば4組になる

Aの組合せはL1とR1、L1とR2、L2とR1、L2とR2の4組です。learnersは研修候補者、coursesはグループ別の研修候補を表す架空データで、同じグループの行を対応させます。

learners.idgrp
L1A
L2A
L3B
L4NULL
L5C
courses.idgrptopic
R1AX
R2AY
R3BZ
R4NULLW

Aの研修候補者はL1とL2の2人で、Aの研修はR1とR2の2つです。L1にはR1とR2、L2にもR1とR2が対応します。同じAでも、候補者や研修が同じ行になるわけではありません。

INNER JOINの答えは5行でNULL同士の組は含まない

重複キーの掛け合わせ、未対応左行の補完、COUNT対象の違いを順に選び分けて行数を検算できる。
SELECT l.id AS learner_id, r.id AS course_id
FROM learners AS l
INNER JOIN courses AS r ON l.grp = r.grp
ORDER BY l.id, r.id;

Aの4組とBの1組を足して5行です。L1とR1、L1とR2のように行IDで数えると、同じAを1件にまとめる誤りを防げます。各比較の真・偽・不明は下の対応表に示しています。

対応する組とINNER・LEFTの結果を開く
左の行右の行l.grp=r.grp
L1・L2(A)R1・R2(A)真。2×2=4組
L1・L2(A)R3(B)偽。2組
L1・L2(A)R4(NULL)不明。2組
L3(B)R1・R2(A)偽。2組
L3(B)R3(B)真。1組
L3(B)R4(NULL)不明。1組
L4(NULL)R1〜R4(全行)不明。4組
L5(C)R1〜R3(A・A・B)偽。3組
L5(C)R4(NULL)不明。1組
同じ結果になる比較をまとめて掲載。左5行×右4行の20組を全て含む
learner_idcourse_id結果に含まれる方式
L1R1INNER・LEFT
L1R2INNER・LEFT
L2R1INNER・LEFT
L2R2INNER・LEFT
L3R3INNER・LEFT
L4NULLLEFTのみ。対応する右行がない
L5NULLLEFTのみ。対応する右行がない

等号比較が真になるのは、Aの2×2=4組とBの1×1=1組です。INNER JOINはこの5組を返します。Aという値を1件にまとめると、別々の行IDから作る組を落としてしまいます。

L4とR4は両方NULLでも、l.grp=r.grpが真にはなりません。通常の比較でNULLが関わると不明となる扱いを、PostgreSQLの公式比較演算子資料でも確認できます。

LEFT JOINは成立5組と未対応の左2行で7行

SELECT l.id AS learner_id, r.id AS course_id
FROM learners AS l
LEFT JOIN courses AS r ON l.grp = r.grp
ORDER BY l.id, r.id;

まず前の5組があり、右と1組も対応しなかった左行はL4とL5です。それぞれ右側をNULLにした1行を加えるので、LEFT JOINの結果は5+2=7行になります。

上の結果表でLEFTだけに含まれるのは、L4・NULLとL5・NULLです。L1〜L3に対応した5行はINNER JOINと共通です。

L4の結果はL4とR4が結合した行ではありません。もしR4と結合していればcourse_idはR4になるはずですが、結果のcourse_idはNULLで、右側に対応行がないことを表しています。

LEFT JOINが左の全行を残すというのは、結果が左表と同じ5行になるという意味ではありません。対応する右行が複数あれば、その左行は複数回現れます。PostgreSQLの公式JOIN説明とSQLiteの公式SELECT説明で、対応行と未対応行の扱いを確認しています。

COUNTは行・対応組・人数で7・5・3に分かれる

COUNT(*)は結合後の7行を数えます。COUNT(r.id)は右IDがNULLでない5行を数え、候補のある人数を求めるときは左IDの重複を除いた3人になります。

SELECT COUNT(*) AS result_rows, COUNT(r.id) AS matched_pairs
FROM learners AS l
LEFT JOIN courses AS r ON l.grp = r.grp;
式結果数えたもの
COUNT(*)7左外部結合後の全行。L4・L5の補完行を含む
COUNT(r.id)5r.idがNULLではない行。今回は実際に成立した対応組の行
入力表learnersの人数5元の左表の行数。上の2つの式と同じ意味ではない

COUNT(r.id)の5は、研修候補がある人が5人という意味でもありません。対応する候補がある左の行IDはL1・L2・L3の3つで、L1とL2に各2行あるため対応組の数が5になります。

ここでCOUNT(r.id)を使えるのは、元の右表のidにはNULLが入らないと決めているからです。結合後のr.idがNULLなら、相手がなく補われた行だと分かります。もともとNULLが入る列を数えると、相手が見つかった行まで数え落とすことがあります。

もし求めるものが「研修候補に対応した人の数」なら、この例ではINNER JOINで成立した行から左IDの重複を除いて数えます。次のCOUNTは3になります。

SELECT COUNT(DISTINCT l.id) AS learners_with_courses
FROM learners AS l
INNER JOIN courses AS r ON l.grp = r.grp;

このSQLをLEFT JOINに変えると、同じCOUNT(DISTINCT l.id)でも答えは5です。候補のないL4・L5も残るためです。「候補がある人」を数えたいなら、重複を除くだけでなく、候補に対応した行だけを選ぶ必要があります。

右のAを1行増やすと結果は2行増える

元のcoursesへ、id=R5・grp=A・topic=Vの1行だけを追加した別条件を考えます。learnersは変えず、INNER JOINとLEFT JOINの行数、COUNT(r.id)がどう変わるかを考える練習です。

追加後の答えを開く

Aが左2行×右3行=6組となり、Bの1組を加えてINNERは7行です。未対応の左L4・L5は同じなのでLEFTは9行、COUNT(r.id)は7になります。

R5と結び付くのはL1とL2です。左の人数は5人、候補のある左行IDは3つのままで、対応組だけが2つ増えます。INNERからLEFTへ変えたときは、元と同じL4・L5の2行を補います。

右表が1行増えても、結果は1行だけ増えるとは限りません。この条件では追加行に対応する左行が2つあるため、結果は2行増えます。

2行はキーの種類、5行は結合した組の数

INNER JOINを2行とした場合は、AとBというキーの種類を数えています。必要なのはAの2×2とBの1×1を足した5組です。6行とした場合はNULL同士を1組加えた可能性がありますが、その比較は不明です。

LEFT JOINを5行とした場合は、左の同じ行が相手の数だけ繰り返される点を見落としています。COUNT(r.id)が5になるのは、補われた2行の右IDがNULLで、数える対象から外れるためです。入力にあるNULLと外部結合が補ったNULLを行IDで区別すると原因が分かります。

再現用SQLは空の練習用データベースで使う

再現用SQLは、新しく作った空の練習用データベースで実行します。本文のSELECTを試すための自作データを作るSQLです。既存の業務データベースでは実行しないでください。

テーブル定義とINSERTを開く
CREATE TABLE learners (
  id TEXT PRIMARY KEY NOT NULL,
  grp TEXT
);
CREATE TABLE courses (
  id TEXT PRIMARY KEY NOT NULL,
  grp TEXT,
  topic TEXT
);
INSERT INTO learners VALUES
 ('L1','A'),('L2','A'),('L3','B'),('L4',NULL),('L5','C');
INSERT INTO courses VALUES
 ('R1','A','X'),('R2','A','Y'),('R3','B','Z'),('R4',NULL,'W');

再テストは元のデータから開始し、次の1行を追加します。同じidを重ねて追加する練習ではないため、再試行時は新しい練習用データベースから作り直してください。

INSERT INTO courses VALUES ('R5','A','V');

実行結果と手計算が同じかは、learner_idとcourse_idの組で比べられます。本文の対応表は答えを考えるための表で、データベース内部の処理順を表したものではありません。内部の処理方法は、利用するデータベースの実行計画で調べます。

試験では問題文のSQL仕様に合わせて学ぶ

試験問題を解くときは、問題文の定義とIPAが定めるSQLの仕様が基準です。基本情報シラバスVer.9.2にはNULL・表の結合・SELECT・集合関数などが含まれます。IPAの記述仕様はJIS X 3005規格群に従うとしているため、SQLite固有の機能とは区別します。

本稿は明示した等号結合・NULL・COUNTの小さな例に限定し、表示順をそろえるSELECTにはORDER BYを付けています。ORDER BYのない行の並びや暗黙の型変換は一般化しません。独自のNULL比較設定と文字列の大文字小文字の扱いなども、実際の試験問題の定義や注記を確認してください。

行の組合せを数えられるようになったら、手元の教材で条件を増やした問題へ進めます。NULLやCOUNTで数える行が分からなければ、データベースの基礎講義と講師への質問でその違いを学べます。

その場合は基本情報技術者の通信講座比較で、科目Aのデータベース分野と学習質問の対象範囲を比べられます。SQLは今回の科目Aデータベース分野に合う支援を選ぶのがポイントです。

一次資料と練習の範囲

資料の確認日は2026年10月1日です。本文のデータとSQLは自作の練習用で、等号結合・NULL・COUNTの条件を限定して説明しています。

書いて整理したい方には、基本情報のSQLで結合後の行数を数える|重複キーとNULLの練習 回答用シート(PDF)も使えます。

目次