SQLとは?Structured Query Languageの完全ガイド
SNS、銀行、ECサイトなど、日常的に使うほとんどのアプリはリレーショナルデータベースにデータを保存し、SQLで取り出しています。SQLを理解することは、ソフトウェア開発、データ分析、ITでとても役立つスキルです。
目次
SQLとは?
SQLはStructured Query Languageの略です。リレーショナルデータベースでデータを作成、検索、更新、管理するために使われる標準化された言語です。
SQLは宣言型の言語です。取得したいデータを記述すると、データベースエンジンが効率的な取得方法を判断します。そのため、JavaScriptやPythonのように手順を細かく書くプログラミング言語とは性質が異なります。
SQLはPostgreSQL、MySQL、SQLite、SQL Server、Oracle Database、多くの分析システムを支える中核技術として今も広く使われています。
発音
SQLは「sequel」と読む場合も、「S-Q-L」と1文字ずつ読む場合もあります。どちらも広く通じます。チームでは、使っているデータベースコミュニティで一般的な読み方に合わせることが多いです。
SQLの簡単な歴史
SQLは、1970年にEdgar F. Coddが提唱したリレーショナルモデルから始まりました。その後、IBMのDonald ChamberlinとRaymond Boyceがリレーショナルデータを問い合わせるためにSEQUELを作り、これがSQLになりました。
| 年 | 出来事 |
|---|---|
| 1970 | Edgar F. Coddがリレーショナルモデルの論文を発表 |
| 1974 | IBMがSEQUELを開発し、のちにSQLへ改名 |
| 1979 | Oracleが最初の商用SQLデータベースをリリース |
| 1986 | SQLがANSI標準になる |
| 1992 | SQL-92が標準JOINや式の主要機能を追加 |
| 1999 | SQL:1999がトリガー、CTE、再帰クエリを追加 |
| 2016-2023 | 現代のSQL標準がJSONやグラフクエリ機能を追加 |
リレーショナルデータベースの解説
リレーショナルデータベースは、データをテーブルに整理します。各テーブルには通常、次の要素があります:
- 列: name、email、price、created_atなどのフィールドを定義します。
- 行: 1行が1件のレコードです。
- 主キー: 行を一意に識別します。
- 外部キー: テーブル間の行を関連付けます。
users(id, name, email)
orders(id, user_id, total, order_date)
orders.user_id -> users.idこの構造により重複を減らせます。たとえば顧客情報はusersテーブルに一度だけ保存し、ordersテーブルはその顧客をidで参照できます。
SQL構文の基本
SQL文は、よく次のカテゴリに分類されます:
| カテゴリ | コマンド | 目的 |
|---|---|---|
| DQL | SELECT | データを取得する |
| DML | INSERT, UPDATE, DELETE | データを変更する |
| DDL | CREATE, ALTER, DROP | スキーマやテーブルを定義する |
| DCL | GRANT, REVOKE | 権限を制御する |
| TCL | BEGIN, COMMIT, ROLLBACK | トランザクションを管理する |
SELECT users.name, orders.total
FROM users
JOIN orders ON orders.user_id = users.id
WHERE orders.total > 100
ORDER BY orders.total DESC;CRUD操作
CRUDはcreate、read、update、deleteの略です。保存されたデータに対して多くのアプリが行う基本操作です。
-- Create
INSERT INTO products (name, price) VALUES ('Keyboard', 49.99);
-- Read
SELECT id, name, price FROM products WHERE price > 20;
-- Update
UPDATE products SET price = 44.99 WHERE id = 1;
-- Delete
DELETE FROM products WHERE id = 1;フィルタリング、並べ替え、集計
実際のクエリでは、行を絞り込み、結果を並べ替え、データをグループ化し、件数、合計、平均、最小値、最大値などの集計を行います。
WHEREは行を絞り込みます。ORDER BYは結果を並べ替えます。GROUP BYは集計のために行をグループ化します。HAVINGはグループ化後の結果を絞り込みます。
JOIN: テーブルを結合する
JOINは複数テーブルの行を結合します。リレーショナルデータベースがデータをあちこちにコピーせず、現実世界の関係を表現できる理由になる重要な機能です。
- INNER JOIN: 両方のテーブルで一致する行だけを返します。
- LEFT JOIN: 左側テーブルの全行と、右側テーブルで一致する行を返します。
- RIGHT JOIN: 右側テーブルの全行と、左側テーブルで一致する行を返します。
- FULL OUTER JOIN: 可能なところは一致させつつ、両側の行を返します。
SELECT users.name, orders.order_date, orders.total
FROM users
INNER JOIN orders ON orders.user_id = users.id;サブクエリとCTE
サブクエリは、別のクエリの中に書くクエリです。CTEはWITHで一時的な結果セットに名前を付ける仕組みで、複雑なSQLを読みやすく再利用しやすくします。
インデックスとパフォーマンス
インデックスは、多くの場合B-treeのようなデータ構造で、データベースが行をより速く見つける助けになります。読み取りは速くなりますが、追加の保存領域が必要で、INSERT、UPDATE、DELETEのたびにインデックス更新も発生するため書き込みが遅くなることがあります。
CREATE INDEX idx_products_category ON products(category);
EXPLAIN SELECT * FROM products WHERE category = 'Electronics';インデックスのトレードオフ
すべての列にインデックスを付けるべきではありません。WHERE、JOIN、ORDER BY、一意性チェックでよく使う列に集中し、EXPLAINで性能を確認します。
トランザクションとACID
| 性質 | 意味 |
|---|---|
| Atomicity | すべての操作が成功するか、すべてロールバックされる |
| Consistency | データベースが有効な状態から別の有効な状態へ移る |
| Isolation | 同時実行されるトランザクションが互いに壊し合わない |
| Durability | コミット済みの変更がクラッシュ後も残る |
BEGIN TRANSACTION;
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
UPDATE accounts SET balance = balance + 100 WHERE id = 2;
COMMIT;
-- ROLLBACK;SQL方言の比較
SQLは標準化されていますが、各データベースは独自機能や細かな構文差を追加しています。
| 機能 | MySQL | PostgreSQL | SQLite | SQL Server |
|---|---|---|---|---|
| 自動採番 | AUTO_INCREMENT | SERIAL / GENERATED | AUTOINCREMENT | IDENTITY |
| 行数制限 | LIMIT | LIMIT | LIMIT | TOP / OFFSET-FETCH |
| 文字列結合 | CONCAT() | || | || | + |
| Upsert | ON DUPLICATE KEY | ON CONFLICT | ON CONFLICT | MERGE |
| JSON対応 | JSON型 | JSONB | JSON関数 | JSON関数 |
SQLインジェクションとセキュリティ
SQLインジェクションは、ユーザー入力をSQL文字列へ直接連結したときに起こります。攻撃者はクエリの意味を変え、データの読み取り、変更、削除を行える可能性があります。
-- Vulnerable: string concatenation
query = "SELECT * FROM users WHERE name = '" + userInput + "'";
-- Safe: parameterized query
query = "SELECT * FROM users WHERE name = ?";
db.execute(query, [userInput]);必ずパラメータ化クエリを使う
ユーザー入力をSQL文字列に連結してはいけません。プリペアドステートメントやパラメータ化クエリを使うと、入力は実行可能なSQLではなくデータとして扱われます。
ベストプラクティス
- キーワードの大文字化と明確なインデントでSQLを一貫して整形します。SQLフォーマッターが役立ちます。
- 意味の分かりやすいテーブル名と列名を使います。
- 本番クエリでは
SELECT *を避け、必要な列だけを取得します。 - インデックスは戦略的に追加し、
EXPLAINで検証します。 - 一緒に成功または失敗すべき関連変更にはトランザクションを使います。
- ユーザーが入力した値には必ずパラメータ化クエリを使います。
- 定期的にバックアップし、復元手順もテストします。
SQLとNoSQLの違い
MongoDB、Redis、Cassandra、DynamoDBなどのNoSQLデータベースは、リレーショナルモデルが最適でないワークロードで役立ちます。
| 観点 | SQL | NoSQL |
|---|---|---|
| データモデル | 行と列を持つテーブル | ドキュメント、キー・バリュー、グラフ、ワイドカラム |
| スキーマ | 定義済みスキーマ | 柔軟なスキーマ |
| スケーリング | 垂直方向が先になることが多い | 水平方向が先になることが多い |
| 一貫性 | 強いACIDサポート | 結果整合性または調整可能な一貫性が多い |
| 向いている用途 | 構造化データとトランザクション | 柔軟なデータと高スループットのワークロード |
現代の多くのシステムでは両方を使います。ユーザーや注文のようなトランザクション記録にはSQL、キャッシュ、検索、イベント、ドキュメント保存にはNoSQLを使う、といった構成です。
参考資料
- Codd, E. F. A Relational Model of Data for Large Shared Data Banks. https://dl.acm.org/doi/10.1145/362384.362685
- ISO/IEC 9075:2023. Information technology - Database languages - SQL.
- OWASP Foundation. SQL Injection. https://owasp.org/www-community/attacks/SQL_Injection
- PostgreSQL Global Development Group. PostgreSQL Documentation. https://www.postgresql.org/docs/
- MySQL. MySQL Reference Manual. https://dev.mysql.com/doc/