プロへの道 » バックエンドコース » ユニット3

バックエンドコース ユニット3 | 目安: 120時間 (約1.5ヶ月)

データベース ― バックエンドの心臓部

データを「ちゃんと」しまう・探す・直す。この地味な仕事のための専用装置がデータベースで、操る言葉がSQL (エスキューエル) です。SQLは50年使われ続けている言語で、一度覚えれば仕事人生の最後まで使えます。教材はSQLite ― Pythonに標準装備された、ファイル1つで動く本物のデータベースです。

このユニットのゴール SQLでデータの出し入れ・絞り込み・集計ができ、「どんな表に分けてしまうか」(テーブル設計) を自分で考えられるようになる。

進め方の地図

やること 仕上がるもの
1週目 授業1: なぜCSVでは足りないのか + 最初のテーブル 最初のデータベースファイル
2週目 授業2: SELECT ― 探す・並べる・集計する SQL小問8本
3週目 授業3: 書き換える ― INSERT・UPDATE・DELETE 安全な書き換え手順の型
4週目 授業4: テーブル設計 ― 分けてつなぐ 2テーブル構成の設計図
5〜6週目 月末制作 在庫管理ミニデータベース

授業1: なぜCSVでは足りないのか + 最初のテーブル

ユニット2のCSVは便利でした。でも、データが増えて・複数人で使うようになると、3つの壁に当たります ― ①検索が遅くなる ②同時に書き込むと壊れる ③「価格欄に文字」のような不正データを防げない。データベースは、この3つを最初から解決している保管庫です。

例題 (リードつき) | 最初のテーブルを作って、1行しまう
  1. つないで、表を定義するdb1.py を作ります。
    import sqlite3
    
    con = sqlite3.connect("pan.db")
    cur = con.cursor()
    
    cur.execute("""
    CREATE TABLE IF NOT EXISTS items (
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        name TEXT NOT NULL,
        price INTEGER NOT NULL
    )
    """)
    意味: 「itemsという表を作る。idは自動採番、nameは文字 (必須)、priceは整数 (必須)」。IF NOT EXISTS は「もうあれば作らない」の保険です。
  2. 1行しまう
    cur.execute("INSERT INTO items (name, price) VALUES (?, ?)",
                ("メロンパン", 180))
    con.commit()
    ? は値の差し込み口 (プレースホルダ) です。値は必ず?で渡す ― 文字列の連結でSQLを組むと、悪意ある入力に乗っ取られる事故 (SQLインジェクション) の入り口になります。最初から正しい型で覚えましょう。
  3. 取り出す
    for row in cur.execute("SELECT * FROM items"):
        print(row)
    con.close()
    実行すると (1, 'メロンパン', 180) が返ります。idの1は、データベースが自動でつけた番号です。
  4. 2回実行してみるもう一度実行すると、メロンパンが2行になります (idは2)。「実行のたびに増えてよいのか?」― この違和感は正しいです。授業3で直します。
pan.dbの正体: フォルダにできた pan.db という1ファイルが、データベース本体です。コピーすればバックアップ、削除すれば全消去。SQLiteのこの手軽さが、学習にも小さな実務にも最適な理由です。

授業2: SELECT ― 探す・並べる・集計する

SQLの花形はSELECTです。練習用に、パンの森の売上データをデータベースに引っ越しさせてから、質問攻めにします。

例題 (リードつき) | CSVをデータベースに引っ越して、質問する
  1. 引っ越しスクリプトを書くユニット2の知識 (csv.DictReader) で読み、1行ずつINSERTします。
    import csv, sqlite3
    
    con = sqlite3.connect("pan.db")
    cur = con.cursor()
    cur.execute("""
    CREATE TABLE IF NOT EXISTS sales (
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        day TEXT, item TEXT, price INTEGER, count INTEGER, total INTEGER
    )
    """)
    with open("pan-uriage.csv", encoding="utf-8") as f:
        for row in csv.DictReader(f):
            cur.execute(
                "INSERT INTO sales (day, item, price, count, total) VALUES (?, ?, ?, ?, ?)",
                (row["日付"], row["商品名"], int(row["たんか"]),
                 int(row["こすう"]), int(row["うりあげ"])))
    con.commit()
    CSV→データベースの引っ越しは、実務でもよくある仕事そのものです。
  2. 絞り込む (WHERE)
    SELECT day, total FROM sales WHERE item = 'メロンパン';
  3. 並べる (ORDER BY)
    SELECT day, item, total FROM sales ORDER BY total DESC LIMIT 5;
    「売上の大きい順に5件」。DESCが大きい順、LIMITが件数制限です。
  4. 集計する (GROUP BY)
    SELECT item, SUM(total) FROM sales GROUP BY item;
    ユニット2で「数える辞書」で書いた商品別集計が、SQLなら1行です。手で書いた経験があるから、この1行の意味が読めるのです。
演習 2-A (リードつき) | SQL小問8本

お題: salesテーブルに、SQLで質問します。①食パンの行だけ全部 ②売上3000円以上の行 ③5月10日の売上合計 ④商品別の販売個数合計 ⑤日別売上のベスト3 ⑥一番安い商品の名前 ⑦「パン」を名前に含む商品の行 (LIKEを調べます) ⑧行数の合計 (COUNT)。

答え合わせのコツ: ③〜⑤は、ユニット2でPythonの集計パターンで解いた問題と同じです。両方の答えが一致すれば、どちらも正しい ― 自分の過去の成果が検算機になります。

授業3: 書き換える ― INSERT・UPDATE・DELETE

書き換え系のSQLは強力で、強力なぶん危険です。プロの作法は1つ ― UPDATEとDELETEは、同じWHEREでSELECTしてから打つ。先に「どの行が対象になるか」を見るのです。

例題 (リードつき) | 値上げと削除を、安全手順で
  1. 対象を先に見る
    SELECT * FROM items WHERE name = 'メロンパン';
    2行あることを確認します (授業1の手順4で増えた重複です)。
  2. 重複を片づける (DELETE)
    DELETE FROM items WHERE id = 2;
    idを指定すれば1行だけ消せます。もしWHEREを忘れると全行消えます。怖さを知るために、消える前に pan.db をコピーしてバックアップを取り、練習用コピーの方でわざと DELETE FROM items; を体験しておきましょう。知識ではなく体験にしておくのが、事故防止の最強の薬です。
  3. 値上げする (UPDATE)
    UPDATE items SET price = 200 WHERE name = 'メロンパン';
    打つ前のSELECT → UPDATE → もう一度SELECTで確認、の3点セットを必ず守ります。UPDATEもWHEREを忘れると全行が書きかわります。
  4. Pythonからも同じcur.execute("UPDATE items SET price = ? WHERE id = ?", (200, 1)) のように、値は必ず ? で渡します。書き換え後の con.commit() を忘れると保存されません ― 「commitして初めて本当に書かれる」もここで体に入れます。

授業4: テーブル設計 ― 分けてつなぐ

データベース設計の核心は、たった1つの原則です ― 同じ情報を2か所に書かない。売上の表に商品の価格を毎回書いていると、値上げのとき100行直すことになります。商品の表と売上の表に分けて、idでつなぐのが正解です。

例題 (リードつき) | 在庫管理の2テーブルを設計する
  1. 情報を全部書き出す在庫管理に必要な情報: 商品名・価格・入庫した数と日付・出庫した数と日付。
  2. 「もの」と「出来事」に分ける商品 (もの) は1回だけ登録するもの、入出庫 (出来事) は毎日増えるもの。性質が違うので表を分けます。
    products: id, name, price
    moves:    id, product_id, kind ('in'か'out'), count, day
  3. つなぎ方を理解するmovesの product_id に、productsのidを入れます。「メロンパンが3個入庫」は (product_id=1, kind='in', count=3) になります。名前ではなく番号でつなぐから、商品名を変えても履歴は壊れません。
  4. つないで読む (JOIN)
    SELECT products.name, moves.kind, moves.count
    FROM moves JOIN products ON moves.product_id = products.id;
    2つの表を貼り合わせて読むのがJOINです。まずこの1パターンだけ使えれば十分です。
設計の練習は紙で: 新しいお題が来たら、コードの前に紙へ「表の名前と列」を書きます。「もの」と「出来事」に分けるという今日の発想だけで、小さな業務アプリの設計はほとんど乗り切れます。
演習 4-A (ヒントだけ) | 図書貸し出しの設計

お題: 事業所の本棚の貸し出し管理を設計します。必要な情報: 本の題名・借りた人・借りた日・返した日。テーブル構成を紙に書き、CREATE TABLEまで実行します。

ヒント: 「もの」= 本。「出来事」= 貸し出し。返した日は、貸し出しの行に「最初は空 (NULL) で、返却時にUPDATEで埋める」のが定番です。

月末制作: 在庫管理ミニデータベース

月末制作

授業4で設計した2テーブルを使って、メニュー式の在庫管理ツールを完成させます。パンの森の倉庫番になったつもりで作りましょう。

  1. メニュー: ①商品登録 ②入庫 ③出庫 ④在庫一覧 ⑤履歴表示 ⑥終了、の while True: メニューを作ります (ユニット1の型です)。
  2. 在庫の計算: 在庫数は「入庫の合計 − 出庫の合計」で、moves表から計算します (SUMとGROUP BYの出番です)。在庫列を持たずに履歴から計算するのがポイント ― 数字の食い違いが起きません。
  3. 守りを入れる: 在庫以上の出庫を断る・数の入力にtry / except・登録のない商品番号を断る、の3つの防御を入れます。
  4. 検査と納品: 意地悪な操作10連発に耐えたら、スタッフに倉庫番ごっこ (登録 → 入庫 → 出庫 → 一覧) をしてもらい、GitHubにpushして完了です。
行きづまったら部品に戻る: このツールに新しい技術は1つもありません。メニュー (ユニット1) + try / except (ユニット2) + SQL (このユニット) の組み立てだけです。詰まったら「どの部品の問題か」に切り分けましょう。切り分けられたら、もう半分解けています。

つまずきやすいポイント

「保存されていない!」とあわてたら: ほぼ con.commit() 忘れです。書き換え (INSERT / UPDATE / DELETE) のあとは必ずcommit。読み (SELECT) には不要です。
SQLの大文字小文字: SELECTもselectも動きます。ただ、SQL語は大文字・名前は小文字で書くと、読みやすさが段違いです。教材もその流儀で書いています。

発展チャレンジ (余力のある方へ)

発展チャレンジ
  • 「DB Browser for SQLite」という無料ソフトを入れると、pan.dbの中身を表計算ソフトのような画面で眺められます。自分のSQLの結果を目で確かめる検算機になります。
  • 在庫ツールに「在庫が3個以下の商品を警告表示する」機能を足してみましょう (HAVINGという仲間を調べることになります)。
  • 「インデックス」とは何か、なぜ検索が速くなるのかを調べて、3行で説明メモを書いてみましょう。データ量が増えたときの切り札です。

できたチェック