データベースとWebアプリケーション開発の基礎

エグゼクティブサマリー

本教材は、データベースの基礎からWebアプリケーション開発までを15回の授業で体系的に学習するガイドである。SQLによるデータ操作、HTMLによるWebページ作成、PythonとFlaskを用いたバックエンド開発を段階的に習得する。

対象者

学習内容

使用ツール

本教材の特徴

概要と学習目標

データベースとは

データベース(DB)は組織的に管理されたデータの集合である。データベース管理システム(DBMS)はデータベースを効率的に管理するソフトウェアで、多数のユーザが同時に安全にデータへアクセスできる環境を提供する。

学習目標

3つの学習フェーズ

基礎学習フェーズ(第1-10回)

実践学習フェーズ(第11-13回)

応用学習フェーズ(第14-15回)

15回の授業内容

第1回 データベースの基本

この回で学ぶこと:データベースの役割、テーブルの構造、CREATE TABLE文とINSERT文の基本
学習のメリット:データの一元管理、一貫性保持、複数ユーザによる同時アクセスが可能になる

SQLFiddleでの実行手順:

  1. SQLFiddle(http://sqlfiddle.com/)にアクセスする
  2. 左上のドロップダウンから「SQLite」を選択する
  3. 左側のパネルに以下のスキーマ構築用SQLを貼り付け、「Build Schema」をクリックする
  4. 右側のパネルにクエリを入力し、「Run SQL」をクリックして結果を確認する

スキーマ構築用SQL:

-- 第1回:商品テーブルの作成
-- CREATE TABLE文でテーブルの構造を定義する
CREATE TABLE products (
    product_id INTEGER PRIMARY KEY,  -- 主キー:各行を一意に識別
    name TEXT NOT NULL,              -- 商品名(必須)
    price INTEGER NOT NULL,          -- 価格(必須)
    stock INTEGER NOT NULL DEFAULT 0 -- 在庫数(デフォルト値0)
);

-- INSERT文でデータを挿入する
INSERT INTO products (product_id, name, price, stock) VALUES
(1, 'ノートパソコン', 89800, 15),
(2, 'ワイヤレスマウス', 2980, 50),
(3, 'キーボード', 5800, 30),
(4, 'モニター', 24800, 20),
(5, 'Webカメラ', 7800, 25);

実行クエリ例:

-- SELECT文:全データを取得
SELECT * FROM products;

-- 特定の列のみ取得
SELECT name, price FROM products;

第2回 リレーショナルデータベースとSQL入門

この回で学ぶこと:リレーショナルデータベースの概念、SELECT文とINSERT文の使い方

SQLFiddleでの実行手順:

  1. SQLFiddle(http://sqlfiddle.com/)にアクセスする
  2. 左上のドロップダウンから「SQLite」を選択する
  3. 左側のパネルに以下のスキーマ構築用SQLを貼り付け、「Build Schema」をクリックする
  4. 右側のパネルにクエリを入力し、「Run SQL」をクリックして結果を確認する

スキーマ構築用SQL:

-- 第2回:SQL入門
CREATE TABLE products (
    product_id INTEGER PRIMARY KEY,
    name TEXT NOT NULL,
    price INTEGER NOT NULL,
    stock INTEGER NOT NULL DEFAULT 0
);

INSERT INTO products (product_id, name, price, stock) VALUES
(1, 'ノートパソコン', 89800, 15),
(2, 'ワイヤレスマウス', 2980, 50),
(3, 'キーボード', 5800, 30),
(4, 'モニター', 24800, 20),
(5, 'Webカメラ', 7800, 25);

実行クエリ例:

-- 全データの取得
SELECT * FROM products;

-- 特定の列のみ取得
SELECT name, price FROM products;

-- 新しいデータの挿入
INSERT INTO products (product_id, name, price, stock)
VALUES (6, 'ヘッドセット', 4500, 40);

-- 挿入後の確認
SELECT * FROM products;

第3回 データベース設計と正規化

この回で学ぶこと:正規化の概念、正規化前後のテーブル構造の違い、データの重複を排除する設計手法
正規化のメリット:データの重複排除、更新時の整合性保持、ストレージの効率化

SQLFiddleでの実行手順:

  1. SQLFiddle(http://sqlfiddle.com/)にアクセスする
  2. 左上のドロップダウンから「SQLite」を選択する
  3. 左側のパネルに以下のスキーマ構築用SQLを貼り付け、「Build Schema」をクリックする
  4. 右側のパネルにクエリを入力し、「Run SQL」をクリックして結果を確認する

スキーマ構築用SQL:

-- 第3回:正規化の学習

-- ▼ 正規化前のテーブル(問題のある設計)
-- 同じ顧客情報(山田太郎)が複数回登場している
CREATE TABLE orders_before (
    order_id INTEGER PRIMARY KEY,
    customer_name TEXT,
    customer_email TEXT,
    product_name TEXT,
    product_price INTEGER,
    order_date TEXT
);

INSERT INTO orders_before VALUES
(1, '山田太郎', 'yamada@example.com', 'ノートパソコン', 89800, '2023-01-15'),
(2, '山田太郎', 'yamada@example.com', 'マウス', 2980, '2023-01-15'),
(3, '佐藤花子', 'sato@example.com', 'モニター', 24800, '2023-01-20');

-- ▼ 正規化後のテーブル(適切な設計)
-- 顧客・商品・注文を別テーブルに分離
CREATE TABLE customers (
    customer_id INTEGER PRIMARY KEY,
    name TEXT NOT NULL,
    email TEXT UNIQUE NOT NULL
);

CREATE TABLE products (
    product_id INTEGER PRIMARY KEY,
    name TEXT NOT NULL,
    price INTEGER NOT NULL
);

CREATE TABLE orders_after (
    order_id INTEGER PRIMARY KEY,
    customer_id INTEGER NOT NULL,
    product_id INTEGER NOT NULL,
    order_date TEXT NOT NULL
);

INSERT INTO customers VALUES (1, '山田太郎', 'yamada@example.com');
INSERT INTO customers VALUES (2, '佐藤花子', 'sato@example.com');

INSERT INTO products VALUES (1, 'ノートパソコン', 89800);
INSERT INTO products VALUES (2, 'マウス', 2980);
INSERT INTO products VALUES (3, 'モニター', 24800);

INSERT INTO orders_after VALUES (1, 1, 1, '2023-01-15');
INSERT INTO orders_after VALUES (2, 1, 2, '2023-01-15');
INSERT INTO orders_after VALUES (3, 2, 3, '2023-01-20');

実行クエリ例:

-- 正規化前:顧客情報が重複している
SELECT * FROM orders_before;

-- 正規化後:顧客情報は1回のみ保存
SELECT * FROM customers;

-- 正規化後:テーブルを結合してデータを取得
SELECT c.name, p.name AS product, o.order_date
FROM orders_after o
JOIN customers c ON o.customer_id = c.customer_id
JOIN products p ON o.product_id = p.product_id;
正規化の注意点:過度な正規化はJOINが増えてパフォーマンスに影響する場合がある。実務では適切なバランスが重要である。

第4回 データの検索(WHERE句)

この回で学ぶこと:WHERE句による条件指定、比較演算子の使い方
WHERE句のメリット:必要なデータのみを効率的に取得できる

SQLFiddleでの実行手順:

  1. SQLFiddle(http://sqlfiddle.com/)にアクセスする
  2. 左上のドロップダウンから「SQLite」を選択する
  3. 左側のパネルに以下のスキーマ構築用SQLを貼り付け、「Build Schema」をクリックする
  4. 右側のパネルにクエリを入力し、「Run SQL」をクリックして結果を確認する

スキーマ構築用SQL:

-- 第4回:WHERE句の学習
CREATE TABLE products (
    product_id INTEGER PRIMARY KEY,
    name TEXT NOT NULL,
    price INTEGER NOT NULL,
    stock INTEGER NOT NULL DEFAULT 0
);

INSERT INTO products (product_id, name, price, stock) VALUES
(1, 'ノートパソコン', 89800, 15),
(2, 'ワイヤレスマウス', 2980, 50),
(3, 'キーボード', 5800, 30),
(4, 'モニター', 24800, 20),
(5, 'Webカメラ', 7800, 25),
(6, 'ヘッドセット', 4500, 40),
(7, 'USBメモリ', 1280, 100),
(8, '外付けHDD', 8900, 35);

実行クエリ例:

-- 価格が5000円より高い商品
SELECT * FROM products WHERE price > 5000;

-- 在庫が30以上の商品
SELECT * FROM products WHERE stock >= 30;

-- 価格が1000円以上かつ5000円以下(AND)
SELECT * FROM products WHERE price >= 1000 AND price <= 5000;

-- 価格が3000円未満または80000円以上(OR)
SELECT * FROM products WHERE price < 3000 OR price >= 80000;

第5回 SELECT文の応用

この回で学ぶこと:BETWEEN、LIKE、ORDER BYの使い方

SQLFiddleでの実行手順:

  1. SQLFiddle(http://sqlfiddle.com/)にアクセスする
  2. 左上のドロップダウンから「SQLite」を選択する
  3. 左側のパネルに以下のスキーマ構築用SQLを貼り付け、「Build Schema」をクリックする
  4. 右側のパネルにクエリを入力し、「Run SQL」をクリックして結果を確認する

スキーマ構築用SQL:

-- 第5回:SELECT文応用
CREATE TABLE products (
    product_id INTEGER PRIMARY KEY,
    name TEXT NOT NULL,
    price INTEGER NOT NULL,
    stock INTEGER NOT NULL DEFAULT 0
);

INSERT INTO products (product_id, name, price, stock) VALUES
(1, 'ノートパソコン', 89800, 15),
(2, 'ワイヤレスマウス', 2980, 50),
(3, 'ワイヤレスキーボード', 5800, 30),
(4, 'モニター', 24800, 20),
(5, 'Webカメラ', 7800, 25),
(6, 'ヘッドセット', 4500, 40),
(7, 'USBメモリ', 1280, 100),
(8, '外付けHDD', 8900, 35);

実行クエリ例:

-- BETWEEN:価格が1000円から5000円の範囲
SELECT * FROM products WHERE price BETWEEN 1000 AND 5000;

-- LIKE:「ワイヤレス」を含む商品名(%は任意の文字列)
SELECT * FROM products WHERE name LIKE '%ワイヤレス%';

-- ORDER BY:価格の高い順
SELECT * FROM products ORDER BY price DESC;

-- 複数条件での並び替え:在庫数の多い順、同じ場合は価格の安い順
SELECT * FROM products ORDER BY stock DESC, price ASC;

第6回 テーブルの結合(JOIN)

この回で学ぶこと:JOIN句による複数テーブルの結合
JOINのメリット:複数テーブルの情報を統合し、関連データを一度に取得できる

SQLFiddleでの実行手順:

  1. SQLFiddle(http://sqlfiddle.com/)にアクセスする
  2. 左上のドロップダウンから「SQLite」を選択する
  3. 左側のパネルに以下のスキーマ構築用SQLを貼り付け、「Build Schema」をクリックする
  4. 右側のパネルにクエリを入力し、「Run SQL」をクリックして結果を確認する

スキーマ構築用SQL:

-- 第6回:JOINの学習
CREATE TABLE customers (
    customer_id INTEGER PRIMARY KEY,
    name TEXT NOT NULL,
    email TEXT NOT NULL
);

CREATE TABLE products (
    product_id INTEGER PRIMARY KEY,
    name TEXT NOT NULL,
    price INTEGER NOT NULL
);

CREATE TABLE orders (
    order_id INTEGER PRIMARY KEY,
    customer_id INTEGER NOT NULL,
    product_id INTEGER NOT NULL,
    quantity INTEGER NOT NULL DEFAULT 1,
    order_date TEXT NOT NULL
);

INSERT INTO customers VALUES (1, '山田太郎', 'yamada@example.com');
INSERT INTO customers VALUES (2, '佐藤花子', 'sato@example.com');
INSERT INTO customers VALUES (3, '鈴木一郎', 'suzuki@example.com');

INSERT INTO products VALUES (1, 'ノートパソコン', 89800);
INSERT INTO products VALUES (2, 'マウス', 2980);
INSERT INTO products VALUES (3, 'モニター', 24800);

INSERT INTO orders VALUES (1, 1, 1, 1, '2023-01-15');
INSERT INTO orders VALUES (2, 1, 2, 2, '2023-01-15');
INSERT INTO orders VALUES (3, 2, 3, 1, '2023-01-20');
INSERT INTO orders VALUES (4, 3, 2, 3, '2023-01-22');

実行クエリ例:

-- 2つのテーブルを結合:顧客名と注文日を表示
SELECT c.name, o.order_date
FROM customers c
JOIN orders o ON c.customer_id = o.customer_id;

-- 3つのテーブルを結合:注文詳細を表示
SELECT
    c.name AS customer_name,
    p.name AS product_name,
    o.quantity,
    p.price,
    (p.price * o.quantity) AS total
FROM orders o
JOIN customers c ON o.customer_id = c.customer_id
JOIN products p ON o.product_id = p.product_id;

第7回 データの集計(GROUP BY)

この回で学ぶこと:GROUP BY句によるグループ化、集計関数、HAVING句
集計のメリット:売上レポート作成、顧客動向分析、意思決定の支援

SQLFiddleでの実行手順:

  1. SQLFiddle(http://sqlfiddle.com/)にアクセスする
  2. 左上のドロップダウンから「SQLite」を選択する
  3. 左側のパネルに以下のスキーマ構築用SQLを貼り付け、「Build Schema」をクリックする
  4. 右側のパネルにクエリを入力し、「Run SQL」をクリックして結果を確認する

スキーマ構築用SQL:

-- 第7回:集計の学習
CREATE TABLE customers (
    customer_id INTEGER PRIMARY KEY,
    name TEXT NOT NULL
);

CREATE TABLE products (
    product_id INTEGER PRIMARY KEY,
    name TEXT NOT NULL,
    price INTEGER NOT NULL
);

CREATE TABLE orders (
    order_id INTEGER PRIMARY KEY,
    customer_id INTEGER NOT NULL,
    product_id INTEGER NOT NULL,
    quantity INTEGER NOT NULL,
    order_date TEXT NOT NULL
);

INSERT INTO customers VALUES (1, '山田太郎');
INSERT INTO customers VALUES (2, '佐藤花子');
INSERT INTO customers VALUES (3, '鈴木一郎');

INSERT INTO products VALUES (1, 'ノートパソコン', 89800);
INSERT INTO products VALUES (2, 'マウス', 2980);
INSERT INTO products VALUES (3, 'モニター', 24800);

INSERT INTO orders VALUES (1, 1, 1, 1, '2023-01-15');
INSERT INTO orders VALUES (2, 1, 2, 2, '2023-01-15');
INSERT INTO orders VALUES (3, 2, 3, 1, '2023-02-20');
INSERT INTO orders VALUES (4, 1, 2, 1, '2023-02-25');
INSERT INTO orders VALUES (5, 3, 1, 1, '2023-03-10');
INSERT INTO orders VALUES (6, 2, 2, 3, '2023-03-15');

実行クエリ例:

-- 顧客別の注文回数
SELECT customer_id, COUNT(*) AS order_count
FROM orders
GROUP BY customer_id;

-- 顧客別の合計金額
SELECT
    c.name,
    COUNT(o.order_id) AS order_count,
    SUM(p.price * o.quantity) AS total_amount
FROM orders o
JOIN customers c ON o.customer_id = c.customer_id
JOIN products p ON o.product_id = p.product_id
GROUP BY c.customer_id;

-- HAVING:2回以上注文した顧客のみ
SELECT customer_id, COUNT(*) AS order_count
FROM orders
GROUP BY customer_id
HAVING order_count >= 2;

第8回 副問い合わせ(サブクエリ)

この回で学ぶこと:クエリの中にクエリを含めるサブクエリの使い方
サブクエリのメリット:複雑な条件での絞り込み、集計結果を利用した検索が可能

SQLFiddleでの実行手順:

  1. SQLFiddle(http://sqlfiddle.com/)にアクセスする
  2. 左上のドロップダウンから「SQLite」を選択する
  3. 左側のパネルに以下のスキーマ構築用SQLを貼り付け、「Build Schema」をクリックする
  4. 右側のパネルにクエリを入力し、「Run SQL」をクリックして結果を確認する

スキーマ構築用SQL:

-- 第8回:サブクエリの学習
CREATE TABLE products (
    product_id INTEGER PRIMARY KEY,
    name TEXT NOT NULL,
    price INTEGER NOT NULL,
    stock INTEGER NOT NULL
);

INSERT INTO products VALUES (1, 'ノートパソコン', 89800, 15);
INSERT INTO products VALUES (2, 'マウス', 2980, 50);
INSERT INTO products VALUES (3, 'キーボード', 5800, 30);
INSERT INTO products VALUES (4, 'モニター', 24800, 20);
INSERT INTO products VALUES (5, 'Webカメラ', 7800, 25);

実行クエリ例:

-- 平均価格を確認
SELECT AVG(price) AS avg_price FROM products;

-- 平均価格より高い商品を取得(サブクエリ使用)
SELECT * FROM products
WHERE price > (SELECT AVG(price) FROM products);

-- 最高価格の商品を取得
SELECT * FROM products
WHERE price = (SELECT MAX(price) FROM products);

-- 最低価格の商品を取得
SELECT * FROM products
WHERE price = (SELECT MIN(price) FROM products);

第9回 主キーと外部キー

この回で学ぶこと:主キーと外部キーによるテーブル間の関係性定義
キー制約のメリット:
  • 主キー:各レコードの一意性を保証し、重複を防止
  • 外部キー:不正なデータの挿入を防止し、データの整合性を維持

SQLFiddleでの実行手順:

  1. SQLFiddle(http://sqlfiddle.com/)にアクセスする
  2. 左上のドロップダウンから「SQLite」を選択する
  3. 左側のパネルに以下のスキーマ構築用SQLを貼り付け、「Build Schema」をクリックする
  4. 右側のパネルにクエリを入力し、「Run SQL」をクリックして結果を確認する

スキーマ構築用SQL:

-- 第9回:キー制約の学習

-- 親テーブル(主キーを持つ)
CREATE TABLE categories (
    category_id INTEGER PRIMARY KEY,
    category_name TEXT NOT NULL UNIQUE
);

-- 子テーブル(外部キーで親テーブルを参照)
CREATE TABLE products (
    product_id INTEGER PRIMARY KEY,
    name TEXT NOT NULL,
    price INTEGER NOT NULL,
    category_id INTEGER,
    FOREIGN KEY (category_id) REFERENCES categories(category_id)
);

INSERT INTO categories VALUES (1, 'コンピュータ');
INSERT INTO categories VALUES (2, '周辺機器');
INSERT INTO categories VALUES (3, 'ストレージ');

INSERT INTO products VALUES (1, 'ノートパソコン', 89800, 1);
INSERT INTO products VALUES (2, 'マウス', 2980, 2);
INSERT INTO products VALUES (3, 'キーボード', 5800, 2);
INSERT INTO products VALUES (4, 'USBメモリ', 1280, 3);

実行クエリ例:

-- カテゴリと商品を結合して表示
SELECT
    p.name AS product_name,
    p.price,
    c.category_name
FROM products p
JOIN categories c ON p.category_id = c.category_id;

-- カテゴリ別の商品数
SELECT
    c.category_name,
    COUNT(p.product_id) AS product_count
FROM categories c
LEFT JOIN products p ON c.category_id = p.category_id
GROUP BY c.category_id;
注意事項:SQLiteでは外部キー制約を有効にするためにPRAGMA foreign_keys = ON;の実行が必要である。

第10回 HTMLの基礎とフォーム

この回で学ぶこと:HTMLの基本構造、フォーム要素の使い方
HTMLフォームのメリット:ユーザーからのデータ入力を受け付け、サーバーに送信できる

Replitでのオンライン実行手順(第10回)

  1. Replit(https://replit.com/)にアクセスし、アカウントにログインする
  2. 「+ Create Repl」ボタンをクリックする
  3. テンプレートから「HTML, CSS, JS」を選択する
  4. Replに名前を付ける(例:html-form-basic)
  5. 「Create Repl」ボタンをクリックしてプロジェクトを作成する
  6. 左側のファイルツリーで「index.html」ファイルを開く
  7. 以下のHTMLコードを貼り付ける
  8. 画面上部の「Run」ボタンをクリックして結果を確認する

注意:Replitの無料版では、一定時間操作がない場合、アプリケーションが自動的に停止する。再度実行するには「Run」ボタンをクリックする。

index.html:

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>商品管理</title>
</head>
<body>
    <h1>商品一覧</h1>
    <table border="1">
        <tr>
            <th>商品名</th>
            <th>価格</th>
        </tr>
        <tr>
            <td>ノートパソコン</td>
            <td>89,800円</td>
        </tr>
        <tr>
            <td>マウス</td>
            <td>2,980円</td>
        </tr>
    </table>

    <h2>商品登録フォーム</h2>
    <form action="/add" method="post">
        <p>
            <label for="name">商品名:</label>
            <input type="text" id="name" name="name" required>
        </p>
        <p>
            <label for="price">価格:</label>
            <input type="number" id="price" name="price" required>
        </p>
        <p>
            <input type="submit" value="登録">
        </p>
    </form>
</body>
</html>
注意事項:
  • required属性:入力必須を指定する
  • method="post":フォームデータをPOSTメソッドで送信する(次回以降のFlaskで処理)

第11回 PythonとFlask入門

この回で学ぶこと:Flaskの基本構造、ルーティングの仕組み
Flaskのメリット:軽量で学習しやすい、シンプルな構文、柔軟な設計

Replitでのオンライン実行手順(第11回)

  1. Replit(https://replit.com/)にアクセスし、アカウントにログインする
  2. 「+ Create Repl」ボタンをクリックする
  3. テンプレートから「Python」を選択する
  4. Replに名前を付ける(例:flask-basic)
  5. 「Create Repl」ボタンをクリックしてプロジェクトを作成する
  6. 左側のファイルツリーで「main.py」ファイルを開く
  7. 以下のコードをmain.pyに貼り付ける
  8. 画面上部の「Run」ボタンをクリックしてアプリケーションを起動する
  9. 右側のWebviewパネルにアプリケーションの出力が表示される

注意:Replitの無料版では、一定時間操作がない場合、アプリケーションが自動的に停止する。再度実行するには「Run」ボタンをクリックする。

main.py:

from flask import Flask

app = Flask(__name__)

# ルーティング:URLと関数を対応付ける
@app.route('/')
def home():
    return '''
    <h1>Flaskアプリケーション</h1>
    <p>Flaskで作成したWebページです。</p>
    <ul>
        <li><a href="/about">このサイトについて</a></li>
        <li><a href="/products">商品一覧</a></li>
    </ul>
    '''

@app.route('/about')
def about():
    return '''
    <h1>このサイトについて</h1>
    <p>Flaskの学習用サンプルです。</p>
    <p><a href="/">トップに戻る</a></p>
    '''

@app.route('/products')
def products():
    return '''
    <h1>商品一覧</h1>
    <ul>
        <li>ノートパソコン - 89,800円</li>
        <li>マウス - 2,980円</li>
        <li>キーボード - 5,800円</li>
    </ul>
    <p><a href="/">トップに戻る</a></p>
    '''

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000)

第12回 データベースとWebの連携

この回で学ぶこと:FlaskからSQLiteデータベースへの接続、テンプレートを使った動的ページ生成
データベース連携のメリット:データの永続化(保存)、動的なコンテンツ生成

Replitでのオンライン実行手順(第12回)

  1. Replit(https://replit.com/)にアクセスし、アカウントにログインする
  2. 「+ Create Repl」ボタンをクリックする
  3. テンプレートから「Python」を選択する
  4. Replに名前を付ける(例:flask-database)
  5. 「Create Repl」ボタンをクリックしてプロジェクトを作成する
  6. 左側のファイルツリーで「main.py」ファイルを開く
  7. 以下の「main.py」のコードをコピーして貼り付ける
  8. 左側のファイルツリーで「+ Add folder」をクリックし、「templates」フォルダを作成する
  9. templatesフォルダ内に「products.html」ファイルを作成し、以下のHTMLを記述する
  10. 画面上部の「Run」ボタンをクリックしてアプリケーションを起動する
  11. 右側のWebviewパネルに商品一覧が表示されることを確認する

注意:Replitの無料版では、一定時間操作がない場合、アプリケーションが自動的に停止する。再度実行するには「Run」ボタンをクリックする。

main.py:

from flask import Flask, render_template
import sqlite3

app = Flask(__name__)

def init_db():
    """データベースを初期化する"""
    conn = sqlite3.connect('shop.db')
    conn.execute('''
        CREATE TABLE IF NOT EXISTS products (
            product_id INTEGER PRIMARY KEY,
            name TEXT NOT NULL,
            price INTEGER NOT NULL
        )
    ''')
    # テーブルが空の場合のみサンプルデータを挿入
    count = conn.execute('SELECT COUNT(*) FROM products').fetchone()[0]
    if count == 0:
        conn.execute("INSERT INTO products VALUES (1, 'ノートパソコン', 89800)")
        conn.execute("INSERT INTO products VALUES (2, 'マウス', 2980)")
        conn.execute("INSERT INTO products VALUES (3, 'キーボード', 5800)")
        conn.commit()
    conn.close()

@app.route('/')
def index():
    """商品一覧を表示する"""
    conn = sqlite3.connect('shop.db')
    conn.row_factory = sqlite3.Row  # 列名でアクセス可能にする
    products = conn.execute('SELECT * FROM products').fetchall()
    conn.close()
    return render_template('products.html', products=products)

if __name__ == '__main__':
    init_db()
    app.run(host='0.0.0.0', port=5000)

templates/products.html:

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>商品一覧</title>
</head>
<body>
    <h1>商品一覧</h1>
    <table border="1">
        <tr>
            <th>ID</th>
            <th>商品名</th>
            <th>価格</th>
        </tr>
        <!-- Jinja2テンプレート:{% for %}でループ処理 -->
        {% for product in products %}
        <tr>
            <td>{{ product.product_id }}</td>
            <td>{{ product.name }}</td>
            <td>{{ product.price }}円</td>
        </tr>
        {% endfor %}
    </table>
</body>
</html>

第13回 CRUD操作の実装

この回で学ぶこと:データの追加・削除機能、フォーム処理、SQLインジェクション対策
CRUD操作のメリット:アプリケーションの核となるデータ操作機能を実装できる

Replitでのオンライン実行手順(第13回)

  1. Replit(https://replit.com/)にアクセスし、アカウントにログインする
  2. 「+ Create Repl」ボタンをクリックする
  3. テンプレートから「Python」を選択する
  4. Replに名前を付ける(例:flask-crud)
  5. 「Create Repl」ボタンをクリックしてプロジェクトを作成する
  6. 左側のファイルツリーで「main.py」ファイルを開く
  7. 以下の「main.py」のコードをコピーして貼り付ける
  8. 左側のファイルツリーで「+ Add folder」をクリックし、「templates」フォルダを作成する
  9. templatesフォルダ内に「index.html」ファイルを作成し、以下のHTMLを記述する
  10. 画面上部の「Run」ボタンをクリックしてアプリケーションを起動する
  11. 商品の追加・削除機能を試す

注意:Replitの無料版では、一定時間操作がない場合、アプリケーションが自動的に停止する。再度実行するには「Run」ボタンをクリックする。

main.py:

from flask import Flask, render_template, request, redirect, url_for
import sqlite3

app = Flask(__name__)

def get_db():
    """データベース接続を取得する"""
    conn = sqlite3.connect('shop.db')
    conn.row_factory = sqlite3.Row
    return conn

def init_db():
    """データベースを初期化する"""
    conn = get_db()
    conn.execute('''
        CREATE TABLE IF NOT EXISTS products (
            product_id INTEGER PRIMARY KEY AUTOINCREMENT,
            name TEXT NOT NULL,
            price INTEGER NOT NULL
        )
    ''')
    conn.commit()
    conn.close()

@app.route('/')
def index():
    """商品一覧を表示する(Read)"""
    conn = get_db()
    products = conn.execute('SELECT * FROM products').fetchall()
    conn.close()
    return render_template('index.html', products=products)

@app.route('/add', methods=['POST'])
def add_product():
    """商品を追加する(Create)"""
    name = request.form['name']
    price = request.form['price']
    conn = get_db()
    # プレースホルダ(?)でSQLインジェクションを防止
    conn.execute('INSERT INTO products (name, price) VALUES (?, ?)', (name, price))
    conn.commit()
    conn.close()
    return redirect(url_for('index'))

@app.route('/delete/')
def delete_product(product_id):
    """商品を削除する(Delete)"""
    conn = get_db()
    conn.execute('DELETE FROM products WHERE product_id = ?', (product_id,))
    conn.commit()
    conn.close()
    return redirect(url_for('index'))

if __name__ == '__main__':
    init_db()
    app.run(host='0.0.0.0', port=5000)

templates/index.html:

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>商品管理</title>
</head>
<body>
    <h1>商品管理</h1>

    <h2>商品追加</h2>
    <form action="/add" method="post">
        <p>商品名: <input type="text" name="name" required></p>
        <p>価格: <input type="number" name="price" required></p>
        <p><input type="submit" value="追加"></p>
    </form>

    <h2>商品一覧</h2>
    <table border="1">
        <tr>
            <th>ID</th>
            <th>商品名</th>
            <th>価格</th>
            <th>操作</th>
        </tr>
        {% for product in products %}
        <tr>
            <td>{{ product.product_id }}</td>
            <td>{{ product.name }}</td>
            <td>{{ product.price }}円</td>
            <td><a href="/delete/{{ product.product_id }}">削除</a></td>
        </tr>
        {% endfor %}
    </table>
</body>
</html>
セキュリティ上の注意事項:
  • SQLインジェクション対策:プレースホルダ(?)を使用し、ユーザー入力を直接SQL文に埋め込まない
  • 本番環境ではCSRF対策(不正リクエスト防止)も必要である

第14回 データ集計とレポート機能

この回で学ぶこと:SQLによるデータ集計、レポート表示、JSON形式でのデータ出力
レポート機能のメリット:データの傾向把握、意思決定の支援

Replitでのオンライン実行手順(第14回)

  1. Replit(https://replit.com/)にアクセスし、アカウントにログインする
  2. 「+ Create Repl」ボタンをクリックする
  3. テンプレートから「Python」を選択する
  4. Replに名前を付ける(例:flask-report)
  5. 「Create Repl」ボタンをクリックしてプロジェクトを作成する
  6. 左側のファイルツリーで「main.py」ファイルを開く
  7. 以下の「main.py」のコードをコピーして貼り付ける
  8. 左側のファイルツリーで「+ Add folder」をクリックし、「templates」フォルダを作成する
  9. templatesフォルダ内に「report.html」ファイルを作成し、以下のHTMLを記述する
  10. 画面上部の「Run」ボタンをクリックしてアプリケーションを起動する
  11. レポート画面とJSON APIの動作を確認する

注意:Replitの無料版では、一定時間操作がない場合、アプリケーションが自動的に停止する。再度実行するには「Run」ボタンをクリックする。

main.py:

from flask import Flask, render_template, jsonify
import sqlite3

app = Flask(__name__)

def init_db():
    """データベースを初期化し、サンプルデータを挿入する"""
    conn = sqlite3.connect('sales.db')
    conn.execute('''
        CREATE TABLE IF NOT EXISTS orders (
            order_id INTEGER PRIMARY KEY,
            product_name TEXT NOT NULL,
            quantity INTEGER NOT NULL,
            price INTEGER NOT NULL,
            order_month TEXT NOT NULL
        )
    ''')
    count = conn.execute('SELECT COUNT(*) FROM orders').fetchone()[0]
    if count == 0:
        orders = [
            (1, 'ノートパソコン', 2, 89800, '2023-01'),
            (2, 'マウス', 10, 2980, '2023-01'),
            (3, 'キーボード', 5, 5800, '2023-01'),
            (4, 'ノートパソコン', 3, 89800, '2023-02'),
            (5, 'マウス', 8, 2980, '2023-02'),
            (6, 'モニター', 4, 24800, '2023-02'),
        ]
        conn.executemany('INSERT INTO orders VALUES (?, ?, ?, ?, ?)', orders)
        conn.commit()
    conn.close()

@app.route('/')
def report():
    """売上レポートを表示する"""
    conn = sqlite3.connect('sales.db')
    conn.row_factory = sqlite3.Row

    # 月別売上集計
    monthly = conn.execute('''
        SELECT order_month, SUM(quantity * price) AS total
        FROM orders
        GROUP BY order_month
        ORDER BY order_month
    ''').fetchall()

    # 商品別売上集計
    by_product = conn.execute('''
        SELECT product_name, SUM(quantity) AS qty, SUM(quantity * price) AS total
        FROM orders
        GROUP BY product_name
        ORDER BY total DESC
    ''').fetchall()

    conn.close()
    return render_template('report.html', monthly=monthly, by_product=by_product)

@app.route('/api/sales')
def api_sales():
    """売上データをJSON形式で返す"""
    conn = sqlite3.connect('sales.db')
    conn.row_factory = sqlite3.Row
    rows = conn.execute('''
        SELECT product_name, SUM(quantity * price) AS total
        FROM orders
        GROUP BY product_name
    ''').fetchall()
    conn.close()
    # dict(row)でsqlite3.RowをPythonの辞書に変換
    return jsonify([dict(row) for row in rows])

if __name__ == '__main__':
    init_db()
    app.run(host='0.0.0.0', port=5000)

templates/report.html:

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>売上レポート</title>
</head>
<body>
    <h1>売上レポート</h1>

    <h2>月別売上</h2>
    <table border="1">
        <tr><th>月</th><th>売上合計</th></tr>
        {% for row in monthly %}
        <tr>
            <td>{{ row.order_month }}</td>
            <td>{{ row.total }}円</td>
        </tr>
        {% endfor %}
    </table>

    <h2>商品別売上</h2>
    <table border="1">
        <tr><th>商品名</th><th>販売数</th><th>売上合計</th></tr>
        {% for row in by_product %}
        <tr>
            <td>{{ row.product_name }}</td>
            <td>{{ row.qty }}</td>
            <td>{{ row.total }}円</td>
        </tr>
        {% endfor %}
    </table>

    <h2>API</h2>
    <p><a href="/api/sales">JSON形式で取得</a></p>
</body>
</html>

第15回 パフォーマンス最適化とまとめ

この回で学ぶこと:インデックスによる検索の高速化、ビューによるクエリの簡素化、学習の総まとめ
インデックスのメリット:検索処理の高速化(特に大量データの場合に効果的)

SQLFiddleでの実行手順:

  1. SQLFiddle(http://sqlfiddle.com/)にアクセスする
  2. 左上のドロップダウンから「SQLite」を選択する
  3. 左側のパネルに以下のスキーマ構築用SQLを貼り付け、「Build Schema」をクリックする
  4. 右側のパネルにクエリを入力し、「Run SQL」をクリックして結果を確認する

スキーマ構築用SQL:

-- 第15回:パフォーマンス最適化
CREATE TABLE products (
    product_id INTEGER PRIMARY KEY,
    name TEXT NOT NULL,
    price INTEGER NOT NULL,
    category TEXT NOT NULL
);

CREATE TABLE orders (
    order_id INTEGER PRIMARY KEY,
    product_id INTEGER NOT NULL,
    quantity INTEGER NOT NULL,
    order_date TEXT NOT NULL
);

INSERT INTO products VALUES (1, 'ノートパソコン', 89800, 'PC');
INSERT INTO products VALUES (2, 'マウス', 2980, '周辺機器');
INSERT INTO products VALUES (3, 'キーボード', 5800, '周辺機器');
INSERT INTO products VALUES (4, 'モニター', 24800, '周辺機器');
INSERT INTO products VALUES (5, 'USBメモリ', 1280, 'ストレージ');

INSERT INTO orders VALUES (1, 1, 2, '2023-01-15');
INSERT INTO orders VALUES (2, 2, 5, '2023-01-20');
INSERT INTO orders VALUES (3, 1, 1, '2023-02-10');
INSERT INTO orders VALUES (4, 3, 3, '2023-02-15');
INSERT INTO orders VALUES (5, 4, 2, '2023-03-01');

-- インデックスの作成(検索性能向上)
CREATE INDEX idx_products_price ON products(price);
CREATE INDEX idx_orders_date ON orders(order_date);

-- ビューの作成(複雑なクエリを簡素化)
CREATE VIEW order_summary AS
SELECT
    p.name AS product_name,
    p.category,
    SUM(o.quantity) AS total_qty,
    SUM(o.quantity * p.price) AS total_amount
FROM orders o
JOIN products p ON o.product_id = p.product_id
GROUP BY p.product_id;

実行クエリ例:

-- ビューを使用:複雑なJOINと集計を簡単に実行
SELECT * FROM order_summary;

-- インデックスが使用される検索
SELECT * FROM products WHERE price > 5000;

-- カテゴリ別の売上(ビューを活用)
SELECT category, SUM(total_amount) AS category_total
FROM order_summary
GROUP BY category;

-- 実行計画の確認
EXPLAIN QUERY PLAN
SELECT * FROM products WHERE price > 5000;
インデックス作成時の注意事項:
  • 頻繁に検索条件として使用する列に作成する
  • インデックスが多すぎるとINSERT/UPDATE/DELETE処理が遅くなる
  • 小さなテーブルではインデックスの効果が小さい
学習のまとめ:
  • リレーショナルデータベースの基礎(テーブル設計、正規化、キー制約)
  • SQLによるデータ操作(SELECT、INSERT、UPDATE、DELETE、JOIN、GROUP BY)
  • Webアプリケーション開発(HTML、Flask、テンプレート、CRUD操作)
  • パフォーマンス最適化(インデックス、ビュー)
今後の学習の展望:
  • NoSQL:MongoDB、Redisなどの非リレーショナルデータベース
  • ORM(Object-Relational Mapping):SQLAlchemyを用いたオブジェクト指向的なデータベース操作
  • クラウドデータベース:AWS RDS、Google Cloud SQLなどのマネージドサービス