SQLとは
SQLは、リレーショナルデータベースへ命令を伝えるための言語です。データは表に似た「テーブル」へ保存されます。横一件分を行、名前や価格のような項目を列と呼びます。
SELECTでデータを検索するINSERTで新しい行を追加するUPDATEで保存済みの値を変更するDELETEで不要な行を削除するCREATE TABLEで保存場所を定義する
SQLのキーワードは大文字・小文字を区別しない製品が一般的です。この講座では、列名と見分けやすいようキーワードを大文字で書きます。
SQLiteで実行してみよう
SQLiteは、サーバーを別に起動せず、一つのファイルで試せるデータベースです。ターミナルでsqlite3 shop.dbを実行すると、練習用のshop.dbを開けます。.headers onと.mode columnは結果を表形式で見やすくするSQLiteの設定です。終了は.quitです。
$ sqlite3 shop.db
SQLite version 3.x.x
sqlite> .headers on
sqlite> .mode column
sqlite> SELECT 'SQLを勉強中' AS message;
message
-------------
SQLを勉強中
sqlite> .quit商品テーブルを作る
商品番号、商品名、価格、分類、在庫数を保存するproductsテーブルを作ります。PRIMARY KEYは行を一意に識別し、NOT NULLは値の省略を禁止します。
CREATE TABLE products (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
price INTEGER NOT NULL CHECK (price >= 0),
category TEXT,
stock INTEGER NOT NULL DEFAULT 0
);
INSERT INTO products (id, name, price, category, stock) VALUES
(1, 'SQL入門書', 2800, 'book', 5),
(2, 'ノート', 300, 'stationery', 12),
(3, 'ボールペン', 150, 'stationery', 0),
(4, 'マグカップ', 900, NULL, 3);価格をINTEGERにしているため、この例では円単位の整数として保存します。お金を浮動小数点数で保存すると丸め誤差が起こる場合があります。
SQLiteの型と型親和性
プログラミング言語の変数の型とは異なり、SQLの型はテーブルの各列へどのような値を保存するかを表します。ただし、SQLiteは値そのものに格納クラスを持たせる、柔軟な型の仕組みを採用しています。
| 格納クラス | 保存する値 | この講座での例 |
|---|---|---|
INTEGER | 符号付き整数 | 商品番号、価格、在庫数 |
REAL | 浮動小数点数 | 平均値や測定値 |
TEXT | 文字列 | 商品名、分類、ISO 8601形式の日付 |
BLOB | 入力したままのバイナリデータ | 画像や暗号化済みデータなど |
NULL | 値が不明または存在しない状態 | 未設定の分類 |
typeof()を使うと、SQLiteが実際の値をどの格納クラスとして扱っているか確認できます。
SELECT typeof(42) AS integer_type,
typeof(3.14) AS real_type,
typeof('SQL') AS text_type,
typeof(X'53514C') AS blob_type,
typeof(NULL) AS null_type;integer_type real_type text_type blob_type null_type
------------ --------- --------- --------- ---------
integer real text blob null宣言した型は「型親和性」として働く
SQLiteで列へINTEGERやTEXTを宣言すると、その型へ変換できる値をなるべく変換して保存する「型親和性」が決まります。列の親和性にはINTEGER、REAL、TEXT、NUMERIC、BLOBがありますが、通常のテーブルでは変換できない別種の値も保存できることがあります。
CREATE TABLE affinity_samples (value INTEGER);
INSERT INTO affinity_samples VALUES ('42'), ('変換できない文字列');
SELECT value, typeof(value) AS stored_as
FROM affinity_samples;value stored_as
------------------ ---------
42 integer
変換できない文字列 text文字列の'42'は整数へ変換されますが、変換できない文字列はTEXTのまま保存されます。PostgreSQLなど、宣言した列型と合わない値を原則として拒否するDBMSとは挙動が異なります。SQLiteでもSTRICTテーブルを使うと、受け入れる型をより厳密に制限できます。
SQLiteで日付をTEXTにする理由
SQLiteには専用のDATE格納クラスがありません。この講座では、文字列の並び順と日付順が一致し、日付関数でも扱いやすいYYYY-MM-DDのISO 8601形式をTEXTへ保存します。時刻を含める場合も形式とタイムゾーンを統一することが重要です。PostgreSQLやMySQLなどにはDATE、TIME、TIMESTAMPといった専用型があるため、利用するDBMSに合う型を選びます。
NOT NULLやCHECK、必要に応じてSTRICTテーブルを使って、保存できる値のルールを表します。SELECTでデータを検索する
SELECTの後ろに表示する列、FROMの後ろに読み取るテーブルを書きます。必要な列だけを指定すると、結果の意味が明確になります。
SELECT name, price
FROM products;name price
---------- -----
SQL入門書 2800
ノート 300
ボールペン 150
マグカップ 900SELECT *は全列を返します。内容を手早く確認するときには便利ですが、アプリでは列の追加によって結果が変わるため、通常は必要な列を列挙します。
計算結果と別名
SELECT name, price, price * 110 / 100 AS price_with_tax
FROM products;name price price_with_tax
---------- ----- --------------
SQL入門書 2800 3080
ノート 300 330
ボールペン 150 165
マグカップ 900 990ASで計算結果に分かりやすい列名を付けられます。
WHEREで行を絞り込む
WHEREは条件を満たす行だけを対象にします。文字列はシングルクォートで囲みます。
SELECT name, price, stock
FROM products
WHERE price >= 500 AND stock > 0;name price stock
---------- ----- -----
SQL入門書 2800 5
マグカップ 900 3=、<>、<、<=、>、>=で比較するANDで両方、ORでどちらかを満たす条件にするBETWEEN 100 AND 500で範囲を指定するIN ('book', 'stationery')で候補を並べるLIKE 'SQL%'で「SQLから始まる」文字列を探す
ANDはORより先に評価されます。意図が伝わりにくい組み合わせは、WHERE (category = 'book' OR category IS NULL) AND stock > 0のように丸括弧を使います。
ORDER BYで並べ替える
テーブルの行に決まった順序はありません。順番が必要ならORDER BYを指定します。ASCは昇順、DESCは降順です。
SELECT name, price
FROM products
ORDER BY price DESC, id ASC
LIMIT 2;name price
---------- -----
SQL入門書 2800
マグカップ 900価格が同じ行の順序も安定させるため、二つ目の基準にidを指定しています。LIMITの構文はDBMSによって異なる場合があります。
NULLを正しく扱う
NULLは、値が不明または存在しない状態です。0や空文字とは異なります。= NULLではなく、IS NULLで調べます。
SELECT name
FROM products
WHERE category IS NULL;name
----------
マグカップNULLを含む算術演算の結果も通常はNULLです。表示用の代替値が必要なら、COALESCE(category, '未分類')のように最初のNULLでない値を返す関数を使えます。
GROUP BYで集計する
集計関数は複数行をまとめます。COUNTは件数、SUMは合計、AVGは平均、MINとMAXは最小・最大を求めます。
SELECT COALESCE(category, '未分類') AS category_name,
COUNT(*) AS product_count,
AVG(price) AS average_price
FROM products
GROUP BY category
ORDER BY category_name;category_name product_count average_price
------------- ------------- -------------
book 1 2800.0
stationery 2 225.0
未分類 1 900.0WHEREは集計前の行を、HAVINGは集計後のグループを絞ります。詳しい例は検索と集計で扱います。
JOINでテーブルを結合する
注文を別テーブルへ保存し、商品番号で商品と関係付けます。外部キーは、存在しない商品番号が保存されるのを防ぐ制約です。
PRAGMA foreign_keys = ON;
CREATE TABLE orders (
id INTEGER PRIMARY KEY,
product_id INTEGER NOT NULL,
quantity INTEGER NOT NULL CHECK (quantity > 0),
ordered_at TEXT NOT NULL,
FOREIGN KEY (product_id) REFERENCES products(id)
);
INSERT INTO orders VALUES
(1, 1, 2, '2026-09-01'),
(2, 2, 3, '2026-09-02');SELECT orders.id, products.name, orders.quantity
FROM orders
INNER JOIN products
ON orders.product_id = products.id
ORDER BY orders.id;id name quantity
-- --------- --------
1 SQL入門書 2
2 ノート 3ONには、どの列同士が同じ関係かを書きます。結合の種類と一対多の集計は結合と副問い合わせで詳しく学びます。
データを追加・更新・削除する
INSERTで追加し、UPDATEで変更し、DELETEで削除します。更新と削除ではWHEREを忘れると全行が対象になります。
INSERT INTO products (id, name, price, category, stock)
VALUES (5, '付せん', 250, 'stationery', 8);
UPDATE products
SET stock = stock - 2
WHERE id = 1 AND stock >= 2;
DELETE FROM products
WHERE id = 5;SELECT id, name, stock FROM products WHERE id = 1;id name stock
-- --------- -----
1 SQL入門書 3UPDATEやDELETEを実行する前に、同じWHEREを使ったSELECTで対象行を確認します。アプリから値を渡すときは文字列連結をせず、プレースホルダーを使ってSQLインジェクションを防ぎます。トランザクションで安全に更新する
複数の変更をまとめて成功または失敗させる仕組みがトランザクションです。BEGINで始め、問題がなければCOMMIT、取り消すならROLLBACKを実行します。
BEGIN;
UPDATE products SET stock = stock - 1
WHERE id = 2 AND stock >= 1;
SELECT id, name, stock FROM products WHERE id = 2;
COMMIT;更新件数が想定と違うときはCOMMITせずROLLBACKします。分離レベルや同時実行時の注意は更新とトランザクションで学びます。
練習問題
問題1:在庫切れの商品
在庫が0の商品名と価格を表示してください。
SELECT name, price
FROM products
WHERE stock = 0;name price
---------- -----
ボールペン 150問題2:在庫金額を求める
各商品の「価格×在庫数」をstock_valueという名前で表示し、大きい順に並べてください。
SELECT name, price * stock AS stock_value
FROM products
ORDER BY stock_value DESC, id ASC;name stock_value
---------- -----------
SQL入門書 8400
ノート 3300
マグカップ 2700
ボールペン 0問題3:注文金額を表示する
商品と注文を結合し、注文番号、商品名、数量、価格×数量の合計を表示してください。
SELECT o.id, p.name, o.quantity,
p.price * o.quantity AS total
FROM orders AS o
INNER JOIN products AS p ON p.id = o.product_id
ORDER BY o.id;id name quantity total
-- --------- -------- -----
1 SQL入門書 2 5600
2 ノート 3 900次に学ぶこと
基本を読み終えたら、次の4テーマで、検索から安全なデータ管理へ理解を広げましょう。
