はじめに
こんにちは!なたでです!
SQL Serverを最近勉強しています。基本的な書き方やちょっとしたつまずきポイント、実務で役立つTIPSをまとめてみました。
初心者が気になる書き方から、自分の備忘録も兼ねて解説しています。これからSQL Serverに取り組む方や、同じ部分で悩んでいる方の参考になればうれしいです。
TIPS
部分一致でレコードを抽出したい
文字列の先頭や一部が一致するデータを探したい時はLIKE句を使います。
SELECT * FROM students WHERE student_name LIKE '山田%'; -- student_nameが「山田」で始まる
レコードを削除したい
DELETE
で削除できます。SELECT *
のような *
は不要です。
DELETE FROM students WHERE grade < 3;
句の順番を知りたい
句(FROM
, WHERE
, ORDER BY
など)は順番があります。基本形は以下のような形です。
SELECT カラム名 FROM テーブル名 [JOIN テーブル名 ON 結合条件] [WHERE 条件] [GROUP BY ...] [HAVING ...] [ORDER BY ソート条件]
UPDATEとINSERTの違い
レコードを更新するUPDATEと挿入するINSERTの書き方は次の違いがあります。
-- 新規追加 INSERT INTO students (student_name, email) VALUES ('鈴木太郎', 'taro.suzuki@example.com'); \-- 既存データの更新 UPDATE students SET email = 'newmail@example.com' WHERE student\_name = '鈴木太郎';
INSERTで列名を省略する
すべての列に順番通り値を入れるなら、列名を省略しても動作します。テーブルの列の順番や数が1つでも変わるとエラーや誤登録につながるので、実務では省略せずに列名を書くのが安全です。
-- カラム名を省略(すべての列を順番通りに埋める場合のみ) INSERT INTO subjects VALUES ('S001', '数学', 3, '2024-04-01');
INSERTでカラムのレコードを省略する
一部の列だけ値を指定した場合、指定しなかった列(たとえばcredits
やcreated_at
など)はNULL
やデフォルト値になります。
INSERT INTO subjects (subject_id, subject_name) VALUES ('S002', '英語');
INSERTで複数レコードを追加する
通常のINSERT
文は1行だけですが、SQL Server(や多くのDBMS)では複数のレコードを一度に追加する書き方もできます。
INSERT INTO classes (class_id, class_name, teacher_id) VALUES ('C001', '1年A組', 'T001'), ('C002', '1年B組', 'T002'), ('C003', '2年A組', 'T003');
UPDATEで複数のカラムを同時に変える
,
で区切ることで複数のカラムを同時に変更できます。
UPDATE subjects SET credits = 4, created_at = '2024-04-01' WHERE subject_id = 'S001';
予約語のカラム名を使いたい
カラム名が予約語(例:KEY
など)の場合は、角括弧[]
で囲む必要があります。
SELECT [LEVEL], subject_name FROM subjects WHERE [LEVEL] = 3;
最初/最後の100件だけ取得する
TOP句とORDER BYを組み合わせて取得できます。
-- 上から100件 SELECT TOP 100 * FROM students ORDER BY enrolled_at ASC; -- 下から100件(最新100件) SELECT TOP 100 * FROM students ORDER BY enrolled_at DESC;
ソート順を複数指定する
「まずAでソート、Aが同じ時はBでソート…」という複数条件の並び替えは、ORDER BY カラム1, カラム2 ...
のようにカンマで並べればOKです。
たとえば「クラスごと、その中で点数が高い順」であれば、次のような順番になります。
- まずは、
class_name(クラス名)
で昇順(A→Z順ASC
) - クラス内では
score(点数)
が高い順(降順DESC
)
SELECT * FROM students ORDER BY class_name ASC, score DESC;
NULLの扱いたい
NULL
との比較は必ずIS NULL
やIS NOT NULL
を使います。=
は使えないので注意。
-- NG: WHERE graduated_at = NULL -- OK: WHERE graduated_at IS NULL
指定した条件で集計したい
GROUP BY
というグループ化句と、SUM
やCOUNT(*)
と呼ばれる集約関数を組み合わせます。
例えば、クラスごとの生徒数を出したい時は以下のように記載します。
SELECT class_name, COUNT(*) AS student_count FROM students GROUP BY class_name;
集約関数の例
SUM(カラム)
:合計値COUNT(*)
:件数MAX(カラム)
:最大値MIN(カラム)
:最小値AVG(カラム)
:平均値
集約関数を指定しない場合
以下のような場合は「class_name(クラス名)の重複を消した一覧」しか返ってきません。
SELECT class_name FROM students GROUP BY class_name;
これはDISTINCTと同じ動作となり、動作はするものの意味はない文となります。
レコードが存在するかチェックしたい
条件に一致するデータがあるかだけ知りたい時はSELECT 1
が使えます。
SELECT 1 FROM subjects WHERE subject_name = '物理';
WHERE文で条件を追加したい
WHERE
文では、AND
やOR
で条件追加が可能です。&といったものは利用できませんし、ORDER BY
のソート順で利用したような,
も利用できません。
-- OK WHERE is_active = 1 AND grade >= 3 -- NG WHERE is\_active = 1, grade >= 3 -- ←エラー
動的に変更したい場合、SQL文ではありませんが、コード上で作成すると楽です。C#では条件を配列にして組み立てると、AND/ORの抜けや重複を防げます
var whereList = new List<string>(); if (minScore != null) whereList.Add("score >= @MinScore"); if (maxScore != null) whereList.Add("score <= @MaxScore"); string whereClause = string.Join(" AND ", whereList);
内部結合と外部結合を知りたい
内部結合と外部結合のイメージ
厳密ではありませんが、AND/OR的な感覚な動作をします。
- 内部結合(
INNER JOIN
) =AND
イメージ- → 両方にないと出力されない
- 外部結合(
LEFT JOIN
) =OR
イメージ(左側優先)- → 片方(主に左)にだけあれば、右が
NULL
でも出る
- → 片方(主に左)にだけあれば、右が
内部結合(INNER JOIN)
両方のテーブルに合致するデータだけが抽出、片方にしかなければ結果に出ません。
SELECT s.student_id, s.student_name, c.class_name FROM students AS s INNER JOIN classes AS c ON s.class_id = c.class_id;
students
にだけいる生徒 → 出力されないclasses
にだけあるクラス → 出力されない- 両方に合致 → 出力される
外部結合(LEFT JOIN)
左側(FROM
で書いた方)のデータは必ず全部出て、右側(JOIN
側)は無ければNULL
で表示されます。
SELECT s.student_id, s.student_name, c.class_name FROM students AS s LEFT JOIN classes AS c ON s.class_id = c.class_id;
students
にだけいる生徒 → 出力される(classes
側はNULL
)classes
にだけあるクラス → 出力されない(students
が基準)- 両方に合致 → 出力される
サブクエリを使用したい
SQL Serverには、OUTER APPLY
と CROSS APPLY
という便利なJOIN
の仲間があります。これらは次のような『行ごとに違うサブクエリ(副問い合わせ)」の結果を結合したいとき』に使います。
- 「各生徒ごとに、最新のテスト結果だけほしい」
- 「注文ごとに、一番高い商品だけ取得したい」
「親テーブルごとに、明細から1件だけ」といった動的な絞り込みは、普通のINNER JOIN
やLEFT JOIN
では難しいです。JOIN
は基本「結合条件でそのまま結合」するだけなので、「1件だけ」や「上位N件だけ」をテーブルごとに取得が苦手なのです。
そこでAPPLY句を使います。
CROSS APPLY
- 親テーブルの各行に対して、サブクエリの結果があれば結合。なければ親の行ごと出ない
- =
INNER JOIN
的
- =
- 親テーブルの各行に対して、サブクエリの結果があれば結合。なければ親の行ごと出ない
OUTER APPLY
- 親テーブルの各行に対して、サブクエリの結果があれば結合。なければサブクエリ側が
NULL
になる- =
LEFT JOIN
的
- =
- 親テーブルの各行に対して、サブクエリの結果があれば結合。なければサブクエリ側が
利用例
各生徒の「最新の成績だけ」取得したい場合は次のようになります。
SELECT s.student_id, s.student_name, r.score, r.exam_date FROM students AS s OUTER APPLY ( SELECT TOP 1 * FROM results AS r WHERE r.student_id = s.student_id ORDER BY r.exam_date DESC ) AS r;
以下のような動作を行っています。
- 各生徒ごとに「
results
」テーブルからその生徒の最新1件だけを抽出 - 成績が1件もない生徒も一覧に出したいので
OUTER APPLY
を利用CROSS APPLY
に変えた場合は、成績がない生徒は一覧に表示されなくなります(INNER JOIN
と同じイメージ)
まとめ
- 通常の
JOIN
は「テーブル同士の結合」、テーブルごとに何件か選ぶといった柔軟な抽出は不 APPLY
句は「サブクエリで動的に1件だけ抽出」などが可能OUTER APPLY
… サブクエリ結果がなくても親テーブル側は出したいときCROSS APPLY
… サブクエリ結果がない親行は表示したくないとき
主キーを変更したい
主キーは、設定されている組み合わせを解除する必要があります。この組み合わせは主キー制約名で設定されており、いったん解除します。事前に主キー制約名を調べておく必要があります。DBへアクセスソフトで確認が可能な場合が多いです。
ALTER TABLE students DROP CONSTRAINT PK_students_student_id;
そして再度主キーを設定します。
ALTER TABLE students ADD CONSTRAINT PK_students_new PRIMARY KEY (student_id, enrolled_on);
ALTERとは
主キーを変えたり、カラムの型を変えたりする際に、ALTER
が登場します。これは、制約や構造を変更する際に使用するコマンドです。主キーの変更、カラムの追加、削除、型の変更などに利用できます。
カラムの型を変えたい
以下のようにすれば型を変えられることが出来ます。
-- 学年(grade)をINT型からVARCHAR(10)型に変更したい場合 ALTER TABLE students ALTER COLUMN grade VARCHAR(10);
すでに既存データがある場合は変換されますが、変換できない場合エラーが発生します。
変換が出来ないものは特定して修正するか削除する必要があります。例えば、以下は数値へキャストできないデータを特定することができます。
SELECT student_id, grade FROM students WHERE TRY_CAST(grade AS INT) IS NULL AND grade IS NOT NULL;
PostgreSQLなら
今回は、SQL Serverなので使えないのですが、PostgreSQLであればUSING
を使用することで、列を変換する際に型変換も同時に実行できます。せっかくなので紹介します。
以下は文字から数値へ変換させる場合です。
ALTER TABLE students ALTER COLUMN grade TYPE INTEGER USING CAST(grade AS INTEGER);
他にも数値でなければNULL
で変換という形も書けます。
ALTER TABLE students ALTER COLUMN grade TYPE INTEGER USING CASE WHEN grade ~ '^\d+$' THEN grade::integer ELSE NULL END;
おわりに
今回は、SQL Serverの基本的な操作やよく使うTIPSを中心にまとめました。
実際に手を動かしてみると、思ったより細かい落とし穴や書き方の工夫が多いことに気づきます。これからSQLを書く方や、ちょっと忘れてしまった方の助けになれば幸いです。
最後まで読んでいただき、ありがとうございました。
コメント