FlowingDev

SQL徹底解説:データに鋭い質問を投げかける言語

SQLはデータベースと対話するための世界共通言語です。単純な検索から複雑なJOINまで、SQLクエリがどのように構成され、必要なデータを正確に取得するのかを学びましょう。

ツールを試す: SQL Playground

一言でいうと

SQL(構造化問い合わせ言語)とは、構造化されたデータベースに質問(クエリ)を投げかけ、その答えを整形されたテーブル形式で受け取るための、世界共通のプログラミング言語です。

SQLが解決する問題

コンピューティングの黎明期、まだすべてがカオスだった頃、データベースはそれぞれが独自の奇妙な方言を持つ、独立した王国のようなものでした。システムAからデータを取り出すには、そのための特別な呪文を覚えなければなりません。そして会社がシステムBを導入したら、その知識は捨ててまた一から学び直し。これは非効率で混沌としており、開発者たちが(とても大きくてベージュ色の)机をひっくり返したくなるほどでした。

1970年代初頭、IBMの二人の優秀な研究者、ドナルド・チェンバリンとレイモンド・ボイスは、エドガー・F・コッドが提唱した「リレーショナルモデル」という新しいタイプのデータベースモデルに取り組んでいました。その中心的なアイデアは、データをシンプルなテーブル(または「リレーション」)に保存し、それらの間の関連性を定義するというもの。この新しいデータベースと対話するために、彼らはSEQUEL(Structured English Query Language)と名付けた言語を開発しました。

その名前は後にSQLと短縮されましたが、目標は変わりませんでした。それは、データを管理・取得するための、標準化され、そこそこ人間にも読みやすい言語を作ること。これが大ヒットしたのです。突然、あるクエリを書けば、少し手直しするだけでIBMやOracle、あるいは新興企業のデータベースでも動くようになりました。

SQLはデータベースにおける「バベルの塔」問題を解決しました。それは、「何が欲しいか」(あなたが望むデータ)と、「どうやって手に入れるか」(データベースエンジンがそれを探し出して取得する低レベルで面倒な詳細)を切り分ける共通言語(リンガ・フランカ)を生み出したのです。これにより、開発者やアナリストは、ストレージシステムの独自仕様ではなく、自分たちの質問のロジックに集中できるようになったのです。

内部の仕組み

SQLの核心は、それが宣言型言語であるという点にあります。データベースにデータを取得する手順を一つ一つ指示するのではなく、ただ欲しいデータを宣言するだけ。すると、データベースのクエリオプティマイザが、あなたのリクエストを実行するための最も効率的な方法を考えてくれます。これはピザを注文するようなものです。「ペパロニのLサイズを一枚」と伝えるだけで、ピザ屋までの道のりをいちいち指示したりはしませんよね。

典型的なSQL「プレイグラウンド」やブラウザ内ツールは、完全なデータベースエンジンをブラウザのタブ内で直接実行することで、これをさらに一歩進めています。これは現代のWebマジックのなせるすごい技です。

中心となる句:SELECT, FROM, WHERE

SQLクエリの三種の神器です。データを取得するクエリを書くなら、ほとんどの場合、少なくともSELECTとFROMは使うことになるでしょう。

  • SELECT: 取得したいカラム(フィールド)を指定します。
  • FROM: データを取得するテーブルを指定します。
  • WHERE: 行をフィルタリングし、特定の条件に一致するものだけを取得します。

シンプルなemployeesテーブルを想像してみてください。

id name department salary
1 Alice Engineering 90000
2 Bob Marketing 65000
3 Charlie Engineering 110000

Engineering部門に所属する全従業員の名前と給与を見つけるには、次のように書きます。

SELECT name, salary
FROM employees
WHERE department = 'Engineering';

データベースエンジンはこれを見て、employeesテーブルを探し、departmentカラムが 'Engineering' である行をスキャンします。そして、それらの行からnameとsalaryカラムだけを抜き出して、次のように返します。

name salary
Alice 90000
Charlie 110000

点と点をつなぐ:JOINの威力

ここで「リレーショナルデータベース」の「リレーショナル」たる部分が真価を発揮します。データがすべて一つの巨大なテーブルに収まっていることは稀です。通常は、重複を避けるために論理的なテーブルに分割されています(この概念を正規化と呼びます)。JOINは、クエリのためにそれらのテーブルを再びつなぎ合わせるための機能です。

projectsテーブルを追加しましょう。

id project_name lead_employee_id
101 Project Phoenix 3
102 Project Chimera 1

ここでのlead_employee_idは、employeesテーブルのidを「指して」います。プロジェクトとそのリーダーの名前のリストを取得するには、一つのテーブルにクエリを発行するだけでは不十分です。テーブルをJOINする必要があります。

SELECT
  projects.project_name,
  employees.name AS lead_name
FROM projects
INNER JOIN employees ON projects.lead_employee_id = employees.id;

ON句が鍵です。これは、2つのテーブルがどのように関連しているかをデータベースに伝える指示です。エンジンは、IDが一致するデータの結合された一時的なビューを作成し、そこからプロジェクト名と従業員名を取得します。

集約とグループ化:GROUP BYと集約関数

個々の行ではなく、要約情報が欲しい場合があります。そこで登場するのがCOUNT()、SUM()、AVG()、MAX()、MIN()といった集約関数です。これらをGROUP BYと組み合わせると、データ分析のスーパーパワーになります。

各部署に何人の従業員がいるか知りたいですか?

SELECT
  department,
  COUNT(id) AS number_of_employees
FROM employees
GROUP BY department;

データベースはまず、departmentごとに行を論理的にグループ化します。次に、各グループ内でCOUNT(id)関数を適用します。結果は次のようなサマリーテーブルになります。

department number_of_employees
Engineering 2
Marketing 1

ブラウザの中のデータベース:SQLiteとWebAssembly

では、サーバーなしでブラウザツール内でこれがどうやって動くのでしょうか?秘密はWebAssembly(Wasm)にあります。世界で最も人気のあるデータベースエンジンの一つであるSQLiteは、C言語で書かれています。Emscriptenのような特別なコンパイラを使うことで、そのCコードを従来の実行可能ファイルではなく、Wasmモジュールにコンパイルできるのです。

ブラウザはこのWasmモジュールを安全なサンドボックス内で読み込んで実行できます。その結果、ファイルベースでトランザクションも可能な完全なSQLデータベースエンジンがローカルで実行されることになります。クエリを入力しても、それはネットワーク経由で送信されません。メモリ上のSQLiteエンジンに直接渡され、処理されて結果が返されます。高速で、プライベートで、セットアップも不要。学習やテスト、手軽なデータ探索に最適な環境です。

現場での実話

「手っ取り早い」APIモックのケース

あるフロントエンド開発者が、複雑なデータグリッドコンポーネントを構築していました。バックエンドAPIはまだ準備できていませんでしたが、データのJSONスキーマは手元にありました。彼女はソート、フィルタリング、ページネーションをテストする必要がありました。大量のモック用JavaScriptロジックを書く代わりに、彼女はブラウザベースのSQLツールにデータを貼り付け、その場でproductsテーブルを作成しました。そしてSELECT * FROM products WHERE category = 'Gadgets' ORDER BY price DESC LIMIT 20 OFFSET 40のような単純なSQLクエリを書くことができたのです。これは実際のAPIコールを完璧に模倣しており、バックエンドがデプロイされる前にUI全体の構築とテストを完了させることができました。

教訓: ブラウザ内SQLプレイグラウンドは、フロントエンド開発者がバックエンドに依存することなく、現実的なデータソースに対してプロトタイプを開発するための、信じられないほど強力なツールです。

アナリストの秘密兵器

あるビジネスアナリストが、顧客、注文、製品に関する3つの別々のCSVファイルを受け取りました。上司は「先月、カリフォルニアで『SuperWidget 3000』を最も多く購入した顧客は誰か」を知りたがっていました。これをExcelでやろうとすると、VLOOKUP地獄、ピボットテーブル、一時シートの作成など、悪夢のような作業になります。代わりに、このアナリストは3つのCSVすべてをSQLプレイグラウンドにロードし、3つのテーブルを作成しました。そして、3つのテーブルをJOINし、州と製品名でフィルタリングし、顧客でグループ化する、うまく作られた1つのクエリで、数分後には答えを手に入れていました。

教訓: SQLはデータセットを切り分け、分析し、結合するために作られた専用ツールです。何時間もかかるスプレッドシート上での曲芸のような作業を、数行の表現力豊かなコードに変えてくれます。

ひょんなことからDBAに

ある新人プログラマーがパフォーマンス問題をデバッグしていました。アプリのあるページが読み込みに非常に時間がかかっていたのです。コードを見ると、まず全ユーザーのリストを取得し、その後ループ処理でユーザー一人ひとりに対して、そのユーザーの投稿数を取得するためにもう一度データベースコールを行っていました。これは「N+1クエリ問題」と呼ばれるものです。手伝っていたシニア開発者は、ただ修正するだけでなく、SQLプレイグラウンドを開き、usersとpostsという2つの簡単なテーブルを作成し、1つのGROUP BYクエリで全ての投稿数を一度に取得する方法を見せました。その瞬間、新人プログラマーは目から鱗が落ちたのです。

教訓: SQLがどのように考えるかを理解することは、複雑なデータ処理を、そのために高度に最適化されているデータベースにオフロードすることで、より効率的なアプリケーションコードを書くのに役立ちます。

よくある間違いやハマりどころ

  • SELECT *は味方でもあり、敵でもあるフレネミーです。 SELECT * FROM my_table LIMIT 10のように、カラムが何であるかをさっと確認するには便利です。しかし、アプリケーションコードで使うのはパフォーマンスの罠です。不要なデータを取得し、データベースとネットワークに余計な負荷をかけ、誰かがテーブルのカラムを追加、削除、または名前変更した場合にアプリケーションが壊れる原因にもなりえます。必要なカラムは明示的に指定しましょう。
  • UPDATEやDELETEでWHEREを忘れること。 これはSQLを扱う者なら誰もが通る究極の通過儀礼です。DELETE FROM users;は一人のユーザーを削除するのではなく、全てのユーザーを削除します。WHERE句は、データを全消ししてしまうのを防ぐ安全ハーネスです。データを変更するステートメントでは、必ずWHERE句をダブルチェックしましょう。
  • NULLはゼロや空文字列ではありません。 NULLは値の不在を表し、奇妙な3値論理を持っています。my_column = NULLのような比較は期待通りに機能しません。my_column IS NULLまたはmy_column IS NOT NULLを使わなければなりません。これはほとんどすべての初心者がつまずく点です。
  • WHEREとHAVINGを混同すること。 どちらもデータをフィルタリングしますが、異なる段階で機能します。WHEREはGROUP BYでグループ化される前に行をフィルタリングします。HAVINGはグループが作成され、集約された後にそのグループをフィルタリングします。集約関数(例:COUNT(*) > 5)でフィルタリングしたい場合は、HAVING句に書く必要があります。

なぜSQLに注目すべきか

SQLはデータベース管理者だけのものではありません。データ駆動が当たり前の今日、SQLは非常に幅広い職種にとって基本的なスキルとなっています。

  • バックエンド開発者は、APIを構築し、アプリケーションデータを管理するために必要です。
  • フロントエンド開発者は、APIとのやり取りをより効果的に行い、より良いユーザー体験を構築するために、SQLを理解することで恩恵を受けます。
  • データサイエンティストやアナリストは、SQLと共に生き、SQLを呼吸しているようなものです。生データを抽出・探索するための主要な手段です。
  • プロダクトマネージャーは、エンジニアリングのチケットを待つことなく、ユーザー行動に関する自身の疑問に答えるために利用できます。

SQLを知っていると、アプリケーションやビジネスの「真実」に直接アクセスできます。正確な質問を投げかけ、具体的な答えを得ることを可能にするスーパーパワーです。遅いAPIのデバッグ、マーケティングレポートの作成、あるいは単に扱っているデータに興味がある場合でも、SQLは現代の技術ツールキットに不可欠なツールです。

さらに深く知るには

  • Wikipedia: SQL: その歴史、標準、および様々な方言についての概要です。(英語)
  • SQLite: SQLiteが解釈するSQL: 多くのブラウザ内ツールで採用されているSQLiteエンジンで使われるSQLの方言に関する公式ドキュメントです。(英語)
  • SQLBolt: ゼロからSQLを学ぶための、優れたインタラクティブなレッスン集です。(英語)
  • PostgreSQLチュートリアル: より多機能なサーバーベースのSQL方言を学ぶための素晴らしいリソースです。(英語)
  • 大規模共有データバンクのためのデータのリレーショナルモデル: すべての始まりとなった、E. F. コッドによる1970年の独創的な論文。難解ですが、歴史的に非常に重要な読み物です。(英語PDF)
  • Select Star SQL: フレンドリーなアプローチでSQLを教える、楽しく、ビジュアルで、インタラクティブな本です。(英語)

エディターはどうコードを理解するのか

色分けはプログラムの実行ではなく、言語パーサーによるものです。CodeMirror と Lezer がブラウザー内で記号と構文を解析します。候補にはキーワード、対応する場合のローカル名、スニペット、小さな組み込みリファレンスが含まれます。登録済みのシンボルにカーソルを重ねるとシグネチャが表示されます。候補が出ても、その API が実行環境に存在する保証はありません。対応するパーサーは構文エラーを検出しますが、型検査や実行の代わりにはなりません。Ruby と Lua はトークンベースのモードで、診断は限定的です。リモートの言語サーバーは使わず、コードは端末内に留まります。

CodeMirror & Lezer: MIT.

理論はOK。さあ手を動かそう — 100%ブラウザ内で。

ツールを試す: SQL Playground