ふくちゃんのスキル道場

スキルを磨いて厳しい世の中を生き抜く事を目指すブログです。

データベース学習ログ(応用編)

アイキャッチ


こんにちは!
ふくちゃんです。

前回のDB学習に続き、今回はSQLの「応用編」に挑戦しました!
RPGの戦闘ができるようになったら、次は装備を整えたくなりますよね。今回は「武器屋」の仕組みを作りながら、実務でも超重要な技術を学んでいきます。

QUEST: データベース学習ログ(応用編)

今回のテーマは「テーブル結合(JOIN)」「トランザクション」です。

「お金を払ったのに武器がない!」という致命的なバグを防げる様に意識してみました。

応用編:武器屋実装プログラムを表示する
import sqlite3

# 1. データベースの初期設定
def setup_shop_db():
    conn = sqlite3.connect("rpg_shop.db")
    cursor = conn.cursor()

    # 武器マスターテーブル(武器の情報を一括管理)
    cursor.execute("""
        CREATE TABLE IF NOT EXISTS weapons (
            id INTEGER PRIMARY KEY,
            name TEXT,
            bonus_attack INTEGER,
            price INTEGER
        )
    """)

    # プレイヤーテーブル(所持金と武器IDを持たせる)
    cursor.execute("""
        CREATE TABLE IF NOT EXISTS players (
            id INTEGER PRIMARY KEY,
            name TEXT,
            gold INTEGER,
            weapon_id INTEGER,
            FOREIGN KEY (weapon_id) REFERENCES weapons(id)
        )
    """)

    # 学習用にデータをリセット
    cursor.execute("DELETE FROM weapons")
    cursor.execute("DELETE FROM players")
    cursor.execute("INSERT INTO weapons VALUES (1, 'ひのきのぼう', 2, 50)")
    cursor.execute("INSERT INTO weapons VALUES (2, '鋼鉄の剣', 15, 500)")
    cursor.execute("INSERT INTO players VALUES (1, '勇者', 600, NULL)")
    
    conn.commit()
    conn.close()

# 2. 武器購入処理(トランザクション)
def buy_weapon(player_id, weapon_id):
    conn = sqlite3.connect("rpg_shop.db")
    conn.row_factory = sqlite3.Row
    cursor = conn.cursor()

    try:
        # DBから情報を取得
        cursor.execute("SELECT * FROM weapons WHERE id = ?", (weapon_id,))
        weapon = cursor.fetchone()
        cursor.execute("SELECT * FROM players WHERE id = ?", (player_id,))
        player = cursor.fetchone()

        print(f"--- 武器屋へようこそ! ---")
        print(f"【{player['name']}】所持金: {player['gold']}G")
        print(f"購入希望: {weapon['name']} (価格: {weapon['price']}G)")

        if player['gold'] >= weapon['price']:
            print(">> 購入処理を開始します...")
            
            # --- ここからトランザクションの真骨頂 ---
            # ① 所持金を減らす (UPDATE)
            new_gold = player['gold'] - weapon['price']
            cursor.execute("UPDATE players SET gold = ? WHERE id = ?", (new_gold, player_id))
            
            # ② 武器IDをプレイヤーに紐付ける (UPDATE)
            cursor.execute("UPDATE players SET weapon_id = ? WHERE id = ?", (weapon_id, player_id))
            
            # すべての更新が正常に終わったので確定させる
            conn.commit()
            print(">> 購入成功!データベースを更新しました。")
        else:
            print(">> 所持金が足りません!")

    except Exception as e:
        # 途中でエラーが起きたら「購入前」の状態に巻き戻す
        conn.rollback()
        print(f">> エラー発生!処理を取り消しました: {e}")
    
    finally:
        conn.close()

if __name__ == "__main__":
    setup_shop_db()
    buy_weapon(1, 2) # 勇者が鋼鉄の剣(500G)を買う

今回のプログラムで学んだ、エンジニアとして押さえておきたいポイントは以下の3点です。

1. テーブルのリレーションシップ
プレイヤーに直接「武器の名前」を書き込むのではなく、IDで紐付けました。これで武器の性能調整が入っても、マスターデータを1箇所変えるだけで済みます。

2. トランザクション(COMMIT / ROLLBACK)
「お金を払う」と「武器を受け取る」は1つのまとまった処理。片方だけ成功してもう片方が失敗するのを防ぐのがトランザクションの役割です。Oracle DBAの学習でもよく出てくる大事な概念ですね!

3. JOIN(結合)
今回はコード内でのSELECTでしたが、武器の名前を表示するにはプレイヤーと武器のテーブルをガッチャンコする `JOIN` 文を使います。これぞSQLの醍醐味!

AIでベースを作ってもらいながら、「なぜこういう設計にするのか?」を考えることで、単なるコーディング以上の学びがあります。
次は、複数のモンスターが登場する「テーブル結合の応用」にも挑戦してみたいと思います!

システムのメッセージ:データベースのセットアップが完了しました。
--- 武器屋へようこそ! ---
【勇者】所持金: 600G
購入希望: 鋼鉄の剣 (価格: 500G)
>> 購入処理を開始します...
>> 購入成功!データベースを更新しました。