SQL
SQL ・ えすきゅーえる
リレーショナルデータベースへの問い合わせ言語。欲しい結果を宣言し、取り方はDBに任せる。
✦ 30秒で試す — 航海取り出し方を1行も書かずに、欲しいデータが届くこの文を送ると、データベースはどうする?予想してみる概要
SQL(Structured Query Language)は、RDBMSに対してデータの問い合わせや更新を行うための言語です。最大の特徴は「どうやって取るか」の手順ではなく「何が欲しいか」という条件を書く宣言的なスタイルにあります。SELECT name FROM users WHERE age >= 20 と書けば、データベースが最適な取り出し方を自分で考えて結果を返してくれます。
PostgreSQL、MySQL、SQLiteなど製品は違っても、基本文法はISO/IECで標準化されたほぼ共通のSQLです。一度身につければデータベースをまたいで通用する、ソフトウェア開発で最も息の長いスキルの一つと言えます。バックエンド開発だけでなく、データ分析やBI(可視化)ツールの世界でも共通言語として使われています。
なぜ生まれたか
リレーショナルモデルが提唱される以前のデータベースでは、データを取り出す手順——どのレコードからポインタをたどり、どの順で読むか——をプログラマが逐一書く必要がありました。データの物理的な配置を知らなければ問い合わせが書けず、配置が変わればプログラムも壊れる。データを使いたいだけの人にとって、あまりに敷居が高い世界でした。
1970年代、IBMのサンノゼ研究所でリレーショナルモデルを実装する実験システム System R が開発され、その問い合わせ言語として Donald Chamberlin と Raymond Boyce が SEQUEL(1974年。商標の問題で後に SQL と改名)を設計しました。目標は「手続きを知らなくても、欲しい結果を英語に近い形で宣言できること」です。取り出し方の最適化はデータベース側(オプティマイザ。実行計画を立てる部品)の仕事になり、人間は「何が欲しいか」に集中できるようになりました。この分業こそがSQLの発明の本質で、1986年に ANSI、翌1987年に ISO の標準となって以降、リレーショナルデータベースの事実上の共通語であり続けています。
詳細
問い合わせの基本形と評価の順
SQL の中心は SELECT 文です。FROM で対象のテーブルを指定し、WHERE で行を絞り込み、SELECT で欲しい列を選びます。GROUP BY と集計関数(COUNT・SUM など)でグループごとに集計し、HAVING で集計後のグループを絞り、ORDER BY で並べ替え、LIMIT(標準 SQL では FETCH FIRST)と OFFSET で返す行数を切ります。そしてRDBMSの真価を引き出すのが JOIN で、外部キーでつながった複数のテーブルを結合し、「顧客名と注文内容を並べる」といった問い合わせを1文で書けます。
つまずきどころは、書く順(SELECT が先頭)と論理的な評価の順(FROM が先)が一致しないことです。「WHERE では SELECT で付けた別名が使えない」のは、WHERE の段ではまだ SELECT が評価されていないからで、逆に ORDER BY では別名が使えます。「集計した合計で絞りたいのに WHERE に書くとエラーになる」のも同じ理由で、WHERE は束ねる前に1行ずつ見る段、HAVING は束ねた後にグループごとに見る段です。なお、これは結果の意味を決める論理的な順であって、実際の処理の順はオプティマイザが自由に組み替えます。
句の条件を変えて、小さな注文表がどの段で落ち、束ねられ、並び替わるかを確かめてください。
左の SQL の句(▾ の付いた条件)を押して変えてください。その句の段から、注文表が評価の順に流れ直します。
4つの役割
SQLは役割ごとにいくつかの部分言語に分かれています。データを読み書きするDML(SELECT / INSERT / UPDATE / DELETE)、テーブルなどの構造を定義するDDL(CREATE / ALTER / DROP)、権限を制御するDCL(GRANT / REVOKE)、そしてトランザクションを制御するTCL(BEGIN / COMMIT / ROLLBACK)です。日常の開発で最も触れるのはDMLですが、スキーママイグレーション(スキーマ変更)ではDDLを、整合性が重要な処理ではTCLを使います。なお、PostgreSQL のように DDL もトランザクションの中で取り消せる製品と、MySQL のように DDL を実行すると暗黙にコミットされる製品があり、マイグレーションの失敗時の後始末が変わります。
実行計画とパフォーマンス
宣言的な言語であるがゆえに、同じ結果を返すSQLでも実行のされ方は大きく変わります。データベースはSQLを受け取ると、統計情報をもとに「どのインデックスを使うか」「テーブルをどの順で結合するか」を決めた実行計画を立てます。EXPLAIN 文でこの計画を確認でき、「インデックスが使われずテーブル全体を走査していた」といった遅さの原因を突き止められます。SQLが書けることと、速いSQLが書けることの間には、この実行計画を読む力の差があります。
実務での落とし穴
実務上、最も重大な落とし穴がセキュリティです。ユーザーの入力を文字列連結でSQLに埋め込むと、入力に紛れ込ませた命令が実行されるSQLインジェクションの脆弱性になります。必ずプレースホルダ(パラメータ化クエリ)を使い、値と命令を分離するのが鉄則です。テーブル名や ORDER BY の列名のようにプレースホルダにできない部分は、許可した名前の一覧から選ぶ形にします。
また、アプリケーションからはORMを通じてSQLを間接的に発行することが多くなりましたが、ORMが生成するSQLの良し悪しを判断するにも、結局は素のSQLと実行計画を読む力が必要です。方言(LIMIT句の書き方や日付関数など製品ごとの差異)はあるものの、標準SQLの基礎はどの現場でも通用します。「まず標準的な書き方を身につけ、必要になったら方言を調べる」という学び方が効率的です。
試問
カフェの注文表(3店舗・8件)に SQL で問い合わせます。句の条件(▾ の付いた部分)を押すと選択肢が順に切り替わり、注文表がデータベースの評価の順(FROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BY → LIMIT)に流れ直して、各段で行が落ちたり束ねられたりします。GROUP BY 店舗 で店舗ごとに束ね、SUM(金額) が店舗の合計です。
左の SQL の句(▾ の付いた条件)を押して変えてください。その句の段から、注文表が評価の順に流れ直します。
