SQLでSELECTやJOINは書ける。でも、「SQLできます」と言ってよいか迷う。例えばウィンドウ関数を使えないと、できるとは言えないのか。面接や配属先で、どこまで求められるのかも気になる。
最初に確認したいのは、知っている構文の数ではなく、目的に合わせて欲しいデータを取り出せるかです。
なお、私は「SQLできます」の基準を自分なりに決めているので、この記事ではその基準を紹介します。
この記事でわかること
- 「SQLできます」と言える基準と、よく挙がるレベルの目安
- 自分が基準に届いているかを、1つの目的で確かめる方法
- 基準に届くと何が変わるか、履歴書や面接でどう伝えるか
結論:目的ごとに、欲しいデータを抽出・作成できるか
「SQLできます」の基準は、分析やその他の目的ごとに、欲しいデータを抽出・作成できるかです。
いわゆる目的に合わせてテーブルから必要な行と列を取り出し、必要に応じて集計や新しいテーブルを作成できるかです。
SQLを書くのは、何かを見たり決めたりするためのこともあれば、テーブルそのものを作るためのこともあります。そのため、構文をいくつ知っているかではなく、目的に合うデータを作れるかで線を引きます。
例で見る:「9月の店舗ごとの売上」を取り出すまで
目的を「結果の1行・列・期間」に置き換えてから書くと、SQLは目的どおりのデータになります。
この記事では、売上表(sales)を例にします。1行が1件の注文で、amountは金額(円)です。
| order_date | store | amount |
|---|---|---|
| 2026-08-31 | 渋谷 | 3000 |
| 2026-09-01 | 渋谷 | 5000 |
| 2026-09-02 | 新宿 | 4000 |
| 2026-09-15 | 渋谷 | 2000 |
この表には、8月の注文が1件、9月の注文が3件入っています。
目的は「9月の店舗ごとの売上を比べたい」とします。SQLを書く前に、欲しいデータの形を3つ決めます。
| 決めること | この例で決めた内容 |
|---|---|
| 結果の1行が表すもの | 1店舗 |
| 並べる列 | 店舗・注文件数・売上合計 |
| 期間 | 2026年9月1日〜30日 |
この表では、目的の1文が、SQLに書ける形に置き換わっています。
決めた形を、そのままSQLにします。この記事のSQLは、PostgreSQLとMySQLを想定しています。order_dateは日付の列です。
SELECT
store,
COUNT(*) AS orders,
SUM(amount) AS total_amount
FROM sales
WHERE order_date >= '2026-09-01'
AND order_date < '2026-10-01'
GROUP BY store;
期間はWHEREで、1行を1店舗にするのはGROUP BYで書いています。
実行結果
| store | orders | total_amount |
|---|---|---|
| 渋谷 | 2 | 7000 |
| 新宿 | 1 | 4000 |
この結果では、8月31日の注文が期間から外れ、渋谷は2件・7,000円になっています。
件数を足すと3件で、元の表の9月の注文数と合います。
期間を決めずに書くと、8月の注文も混ざり、渋谷は3件・10,000円になります。構文は同じでも、目的と違う数字が出ます。
よく挙がるレベルの目安:構文で分けた初級・中級・上級
「SQLができるレベル」に決まった基準はなく、よく挙がるのは構文で分けた初級・中級・上級の目安です。
求人や面接で「SQLができる」と書かれるとき、指す範囲は会社や職種によって違います。検索して出てくる記事では、次のような分け方がよく挙がります。
| レベル | よく挙がる構文 | できること |
|---|---|---|
| 初級 | SELECT・WHERE・ORDER BY | 1つの表から、行と列を絞って取り出す |
| 中級 | GROUP BY・JOIN・サブクエリ | 集計し、複数の表をつなぐ |
| 上級 | ウィンドウ関数・WITH句・処理速度の改善 | 複雑な集計を、読みやすく速く書く |
この表では、レベルが上がるほど、扱える表と集計が複雑になっています。
ここからは、私の見方です。
技術だけで言えば、DDLとDMLが書ければ「SQLできる」レベルです。処理の速さや負荷まで考えて書いているとなお良いと思います。
DDLはテーブルを作る・変える文(CREATE TABLEなど)です。DMLはデータを取り出す・追加する・更新する文(SELECT・INSERT・UPDATEなど)です。
私が実務で使うのは、基本のDDLとDMLです。ストアドプロシージャはたまに書きます。
なお、ウィンドウ関数はほとんど使わず、WITH句も個人的にはあまり使いません。サブクエリはかなり使います。
感覚でいえば、「SQLができる人」は、どんな処理でどんなSQLを書いたかの履歴を残している人です。コメントもしっかり書いてあり、インデントがきれいに整っているとできる人だと感じます。
何にせよ、目的ごとに欲しいデータを抽出・作成できる人が「SQLができる人」です。
試し方:目的を1つ決めて取り出し、件数とNULLを確かめる
基準に届いているかは、目的を1つ決めて欲しいデータを取り出し、件数とNULLを確かめられるかで分かります。
手順は、次の4つです。
- 仕事か練習の表から、目的を1つ決める
- 結果の1行・列・期間を、先に書き出す
- SQLを書いて、結果を取り出す
- 件数とNULLの行を確かめる
仕事の表が使えないときは、練習用のデータベースでも試せます。取り出したら、次のチェックリストで振り返ります。
- 目的を1文で言える
- 結果の1行が何を表すかを言える
- 集計した件数の合計が、元の表の対象の件数と合う
- NULLの行が、どこに入ったか・消えたかを言える
- 期間と条件が、目的どおりになっている
件数が合わないときの確かめ方は、次の記事にまとめています。
詰まりやすいポイント
- NULLも、ほかの値と同じように扱われると思う
NULLは、結合とGROUP BYで扱いが変わります。結合では、つなぐ列がNULLの行は、=で比べてもどの行とも一致しません。
INNER JOINではその行が消え、LEFT JOINでは行は残り、右の表の列がNULLになります。GROUP BYでは、NULLの行が1つのグループにまとまります。COUNT(列名)は、NULLの行を数えません。
迷ったら、すべての行を数える COUNT(*) から COUNT(列名) を引いて、その列のNULLの件数を先に数えます。
- エラーが出なければ、SQLは合っていると思う
条件の書き方を間違えても、エラーが出ないことがあります。数字だけが目的とずれます。
私も、ANDとORのかっこの位置がずれて、件数が合わなかったことはよくあります。
届かなかったときは:SELECT・WHERE・GROUP BY・JOINから身につける
基準に届くまでに要るのは、SELECT・WHERE・GROUP BY・JOINです。ウィンドウ関数やWITH句は、そのあとで構いません。
SQLを書く段階で手が止まったなら、構文を順番に身につけます。学ぶ順番は、次の記事にまとめています。
もし欲しいデータの形を決める段階で手が止まったなら、SQLを書く前に、結果の表を手で描きます。1行が何か、どの列が要るかが見えると、使う構文も決まります。
ひとりで進めにくいときは、本や講座、スクールで学ぶ方法もあります。スクールを選ぶときの判断基準は、次の記事にまとめています。
業務への着地:欲しいデータを自分で作れると、待ち時間と作り直しが減る
目的から欲しいデータを自分で作れると、データを人に頼んで待つ時間や、目的とずれた数字の作り直しが減ります。
比べると次のとおりです。
| 場面 | Before | After |
|---|---|---|
| 欲しいデータ | 担当者に頼んで待つ | 目的から自分で取り出す |
| 渡す前の確認 | 結果の表だけを見る | 件数とNULLを確かめる |
| 翌月の同じ集計 | もう一度頼むか、書き直す | 残したSQLの期間を変えて実行する |
この表では、データを待つ・作り直す作業が、自分で取り出して確かめる作業に変わっています。
履歴書や面接で「SQLできます」と伝えるときも、この記事の基準をそのまま使います。構文の名前を並べるより、何の目的で、どんなデータを取り出したかを書きます。
まとめ
「SQLできます」と言えるかは、次の5つで確かめます。
- 目的を、結果の1行・列・期間に置き換えられるか
- SELECT・WHERE・GROUP BY・JOINで、データを取り出せるか
- 集計した件数の合計が、元の表と合っているか
- NULLの行がどこに入ったか・消えたかを言えるか
- 何の目的で、どんなデータを取り出したかを説明できるか
届かなかったときは、SELECT・WHERE・GROUP BY・JOINから身につけます。ウィンドウ関数やWITH句は、そのあとで構いません。
次に読む記事
SQLの書き方を、SELECT・WHERE・GROUP BY・JOINの順に身につける勉強方法をまとめています。
生成AIに書いてもらったSQLを、そのまま実行せずに確かめる手順をまとめています。
取り出したデータを分析の仕事でどう使うかを、依頼から報告までの流れでまとめています。

-160x90.png)






