2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

はじめに

image.png

MySQLのViewは、複雑なクエリを仮想的なテーブルとして扱える機能です。今回は、Google Colab上でMySQLを動かし、Viewの基本的な使い方を実際に試してみました。

環境

  • Google Colab / MySQL 8.0 / Python 3.x + PyMySQL

事前準備

# MySQLセットアップ
!apt update && apt install mysql-server -y
!service mysql start
!mysql -u root -e "ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'password';"
!pip install pymysql

image.png

1. テストデータの準備

image.png

import pymysql

connection = pymysql.connect(host='localhost', user='root', password='password', charset='utf8mb4')

try:
    with connection.cursor() as cursor:
        cursor.execute("CREATE DATABASE IF NOT EXISTS view_test")
        cursor.execute("USE view_test")
        
        # レストランテーブル
        cursor.execute("""
            CREATE TABLE IF NOT EXISTS restaurants (
                id INT AUTO_INCREMENT PRIMARY KEY,
                name VARCHAR(100) NOT NULL,
                location VARCHAR(100),
                rating DECIMAL(3,2),
                cuisine_type VARCHAR(50)
            )
        """)
        
        # メニューテーブル
        cursor.execute("""
            CREATE TABLE IF NOT EXISTS menu_items (
                id INT AUTO_INCREMENT PRIMARY KEY,
                restaurant_id INT,
                item_name VARCHAR(100) NOT NULL,
                category VARCHAR(50),
                price DECIMAL(10,2),
                is_available BOOLEAN DEFAULT TRUE,
                FOREIGN KEY (restaurant_id) REFERENCES restaurants(id)
            )
        """)
        
        # 注文テーブル
        cursor.execute("""
            CREATE TABLE IF NOT EXISTS orders (
                id INT AUTO_INCREMENT PRIMARY KEY,
                restaurant_id INT,
                menu_item_id INT,
                order_date DATE,
                quantity INT,
                total_price DECIMAL(10,2),
                FOREIGN KEY (restaurant_id) REFERENCES restaurants(id)
            )
        """)
        
        # サンプルデータ挿入
        restaurants_data = [
            ("カレーハウス太郎", "東京都新宿区", 4.2, "インド料理"),
            ("パスタ工房", "東京都渋谷区", 4.5, "イタリア料理"),
            ("寿司処まる", "東京都銀座", 4.8, "日本料理"),
        ]
        
        menu_items_data = [
            (1, "チキンカレー", "メイン", 1200, True),
            (1, "ナン", "サイド", 300, True),
            (2, "カルボナーラ", "メイン", 1300, True),
            (2, "シーザーサラダ", "サイド", 800, True),
            (3, "特上寿司", "メイン", 3000, True),
            (3, "茶碗蒸し", "サイド", 500, True),
        ]
        
        orders_data = [
            (1, 1, "2024-01-15", 2, 2400),
            (2, 3, "2024-01-16", 1, 1300),
            (3, 5, "2024-01-17", 1, 3000),
        ]
        
        # データ挿入処理
        for data in restaurants_data:
            cursor.execute("INSERT IGNORE INTO restaurants (name, location, rating, cuisine_type) VALUES (%s, %s, %s, %s)", data)
        
        for data in menu_items_data:
            cursor.execute("INSERT IGNORE INTO menu_items (restaurant_id, item_name, category, price, is_available) VALUES (%s, %s, %s, %s, %s)", data)
        
        for data in orders_data:
            cursor.execute("INSERT IGNORE INTO orders (restaurant_id, menu_item_id, order_date, quantity, total_price) VALUES (%s, %s, %s, %s, %s)", data)
        
        connection.commit()
        print("データベース準備完了")

finally:
    connection.close()

2. 基本的なViewの作成

image.png

connection = pymysql.connect(host='localhost', user='root', password='password', database='view_test', charset='utf8mb4')

try:
    with connection.cursor() as cursor:
        # 1. レストランメニュー一覧View
        cursor.execute("DROP VIEW IF EXISTS restaurant_menu_view")
        cursor.execute("""
            CREATE VIEW restaurant_menu_view AS
            SELECT 
                r.name AS restaurant_name,
                r.cuisine_type,
                m.item_name,
                m.category,
                m.price
            FROM restaurants r
            JOIN menu_items m ON r.id = m.restaurant_id
            WHERE m.is_available = TRUE
            ORDER BY r.name, m.category
        """)
        
        # 2. 高評価レストランView
        cursor.execute("DROP VIEW IF EXISTS high_rated_restaurants")
        cursor.execute("""
            CREATE VIEW high_rated_restaurants AS
            SELECT 
                name,
                location,
                rating,
                cuisine_type,
                CASE 
                    WHEN rating >= 4.5 THEN '最高評価'
                    WHEN rating >= 4.0 THEN '高評価'
                    ELSE '普通'
                END AS rating_category
            FROM restaurants
            WHERE rating >= 4.0
            ORDER BY rating DESC
        """)
        
        connection.commit()
        print("基本Viewの作成完了")

finally:
    connection.close()

3. 集計処理のView

image.png

connection = pymysql.connect(host='localhost', user='root', password='password', database='view_test', charset='utf8mb4')

try:
    with connection.cursor() as cursor:
        # レストラン別売上集計View
        cursor.execute("DROP VIEW IF EXISTS restaurant_sales_summary")
        cursor.execute("""
            CREATE VIEW restaurant_sales_summary AS
            SELECT 
                r.name AS restaurant_name,
                r.cuisine_type,
                COUNT(o.id) AS total_orders,
                COALESCE(SUM(o.total_price), 0) AS total_sales,
                COALESCE(AVG(o.total_price), 0) AS avg_order_value
            FROM restaurants r
            LEFT JOIN orders o ON r.id = o.restaurant_id
            GROUP BY r.id, r.name, r.cuisine_type
            ORDER BY total_sales DESC
        """)
        
        connection.commit()
        print("集計Viewの作成完了")

finally:
    connection.close()

4. Viewの実行とデータ確認

connection = pymysql.connect(host='localhost', user='root', password='password', database='view_test', charset='utf8mb4')

try:
    with connection.cursor() as cursor:
        # 1. レストランメニュー一覧
        print("=== レストランメニュー一覧 ===")
        cursor.execute("SELECT * FROM restaurant_menu_view")
        results = cursor.fetchall()
        
        for row in results:
            print(f"{row[0]} - {row[2]} ({row[3]}): {row[4]:,.0f}")
        
        # 2. 高評価レストラン
        print("\n=== 高評価レストラン ===")
        cursor.execute("SELECT * FROM high_rated_restaurants")
        results = cursor.fetchall()
        
        for row in results:
            print(f"{row[0]} - 評価: {row[2]} ({row[4]})")
        
        # 3. 売上集計
        print("\n=== レストラン別売上集計 ===")
        cursor.execute("SELECT * FROM restaurant_sales_summary")
        results = cursor.fetchall()
        
        print("レストラン名 | 料理種類 | 注文数 | 売上合計")
        print("-" * 45)
        for row in results:
            print(f"{row[0][:15]:15} | {row[1][:8]:8} | {row[2]:5} | {row[3]:8.0f}")

finally:
    connection.close()

image.png

5. Viewの管理

connection = pymysql.connect(host='localhost', user='root', password='password', database='view_test', charset='utf8mb4')

try:
    with connection.cursor() as cursor:
        # View一覧の確認
        cursor.execute("SHOW FULL TABLES WHERE Table_type = 'VIEW'")
        views = cursor.fetchall()
        
        print("=== 作成されたView一覧 ===")
        for view in views:
            print(f"📊 {view[0]}")
        
        # 特定のView定義確認
        cursor.execute("SHOW CREATE VIEW restaurant_menu_view")
        result = cursor.fetchone()
        print(f"\n=== View定義例 ===")
        print(f"View名: {result[0]}")
        print(f"定義: {result[1][:80]}...")  # 最初の80文字のみ表示

finally:
    connection.close()

image.png

Viewのメリット・デメリット

Viewの利用には、再利用性やセキュリティの強化、クエリの可読性向上といった利点があります。一方で、複雑なViewはパフォーマンスの低下や更新制限、デバッグの難しさといった課題もあるため、用途に応じた適切な設計が求められます。

まとめ

image.png

Google Colab上でMySQLのViewを試すことで、その柔軟性と実用性を体感できました。通常のクエリとViewを目的に応じて使い分けることで、開発効率を高めつつ、セキュリティ対策にもつなげられます。複雑な処理やアクセス制限にはViewが特に効果的です。

参考情報

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?