Python×SQL完全ガイド:基礎から上級テクニック+エラー解決法

フリーランスボード

20万件以上の案件から、副業に最適なリモート・週3〜の案件を一括検索できるプラットフォーム。プロフィール登録でAIスカウトが自動的にマッチング案件を提案。市場統計や単価相場、エージェントの口コミも無料で閲覧可能なため、本業を続けながら効率的に高単価の副業案件を探せます。フリーランスボード

ITプロパートナーズ

週2〜3日から働ける柔軟な案件が業界トップクラスの豊富さを誇るフリーランスエージェント。エンド直契約のため高単価で、週3日稼働でも十分な報酬を得られます。リモートや時間フレキシブルな案件も多数。スタートアップ・ベンチャー中心で、トレンド技術を使った魅力的な案件が揃っています。専属エージェントが案件紹介から契約交渉までサポート。利用企業2,000社以上の実績。ITプロパートナーズ

Midworks 10,000件以上の案件を保有し、週3日〜・フルリモートなど柔軟な働き方に対応。高単価案件が豊富で、報酬保障制度(60%)や保険料負担(50%)など正社員並みの手厚い福利厚生が特徴。通勤交通費(月3万円)、スキルアップ費用(月1万円)の支給に加え、リロクラブ・freeeが無料利用可能。非公開案件80%以上、支払いサイト20日で安心して稼働できます。Midworks

PythonとSQLの組み合わせは、データベース操作において最も強力で実用的なスキルの一つです。SQLiteからMySQL、PostgreSQLまで、様々なデータベースとPythonを連携させることで、効率的なデータ処理と分析が可能になります。本記事では、PythonでのSQL操作の基礎から高度なテクニック、よくあるエラーと解決方法まで、実践的なサンプルコードとともに詳しく解説します。

Python×SQLの基本概念

SQLとは何か

SQL(Structured Query Language)は、リレーショナルデータベースを操作するための標準的な言語です。PythonからSQLを実行することで、データの追加、取得、更新、削除(CRUD操作)を効率的に行えます。

import sqlite3

# 基本的なデータベース操作
conn = sqlite3.connect('sample.db')
cursor = conn.cursor()

# テーブル作成
cursor.execute('CREATE TABLE users (id INTEGER, name TEXT, age INTEGER)')

# データ挿入
cursor.execute("INSERT INTO users VALUES (1, '田中', 25)")
conn.commit()
conn.close()

Pythonで使用する主要なライブラリ

# SQLite(標準ライブラリ)
import sqlite3

# MySQL
import mysql.connector

# PostgreSQL
import psycopg2

# SQLAlchemy(ORM)
from sqlalchemy import create_engine

# pandas(データ分析)
import pandas as pd

基礎レベル:SQLiteを使った基本操作

データベース接続とテーブル作成

def create_database():
    conn = sqlite3.connect('company.db')
    cursor = conn.cursor()
    
    cursor.execute('''
        CREATE TABLE IF NOT EXISTS employees (
            id INTEGER PRIMARY KEY,
            name TEXT NOT NULL,
            department TEXT,
            salary INTEGER
        )
    ''')
    
    conn.commit()
    conn.close()

基本的なCRUD操作

class EmployeeDB:
    def __init__(self):
        self.conn = sqlite3.connect('company.db')
        self.cursor = self.conn.cursor()
    
    def add_employee(self, name, department, salary):
        self.cursor.execute(
            "INSERT INTO employees (name, department, salary) VALUES (?, ?, ?)",
            (name, department, salary)
        )
        self.conn.commit()
    
    def get_employee(self, employee_id):
        self.cursor.execute("SELECT * FROM employees WHERE id = ?", (employee_id,))
        return self.cursor.fetchone()
    
    def update_salary(self, employee_id, new_salary):
        self.cursor.execute(
            "UPDATE employees SET salary = ? WHERE id = ?",
            (new_salary, employee_id)
        )
        self.conn.commit()
    
    def delete_employee(self, employee_id):
        self.cursor.execute("DELETE FROM employees WHERE id = ?", (employee_id,))
        self.conn.commit()

データの検索とフィルタリング

def search_employees():
    conn = sqlite3.connect('company.db')
    cursor = conn.cursor()
    
    # 条件付き検索
    cursor.execute("SELECT * FROM employees WHERE department = ?", ("開発部",))
    dev_employees = cursor.fetchall()
    
    # 給与範囲での検索
    cursor.execute(
        "SELECT name, salary FROM employees WHERE salary BETWEEN ? AND ?",
        (400000, 600000)
    )
    salary_range = cursor.fetchall()
    
    # 並び替え
    cursor.execute("SELECT * FROM employees ORDER BY salary DESC")
    sorted_employees = cursor.fetchall()
    
    conn.close()
    return dev_employees, salary_range, sorted_employees

中級レベル:複雑なSQL操作

JOINを使ったテーブル結合

def setup_related_tables():
    conn = sqlite3.connect('company.db')
    cursor = conn.cursor()
    
    # 部署テーブル
    cursor.execute('''
        CREATE TABLE IF NOT EXISTS departments (
            id INTEGER PRIMARY KEY,
            name TEXT NOT NULL,
            manager_id INTEGER
        )
    ''')
    
    # INNER JOIN例
    cursor.execute('''
        SELECT e.name, e.salary, d.name as department_name
        FROM employees e
        INNER JOIN departments d ON e.department = d.name
    ''')
    
    conn.commit()
    conn.close()

集約関数とグループ化

def aggregation_queries():
    conn = sqlite3.connect('company.db')
    cursor = conn.cursor()
    
    # 基本統計
    cursor.execute('''
        SELECT COUNT(*), AVG(salary), MAX(salary), MIN(salary)
        FROM employees
    ''')
    stats = cursor.fetchone()
    
    # 部署別集計
    cursor.execute('''
        SELECT department, AVG(salary), COUNT(*)
        FROM employees
        GROUP BY department
    ''')
    dept_stats = cursor.fetchall()
    
    # サブクエリ
    cursor.execute('''
        SELECT name, salary
        FROM employees
        WHERE salary > (SELECT AVG(salary) FROM employees)
    ''')
    above_avg = cursor.fetchall()
    
    conn.close()
    return stats, dept_stats, above_avg

トランザクション処理

def transaction_example():
    conn = sqlite3.connect('company.db')
    
    try:
        conn.execute("BEGIN")
        conn.execute("UPDATE employees SET salary = salary * 1.1 WHERE department = '開発部'")
        conn.execute("INSERT INTO employees (name, department, salary) VALUES (?, ?, ?)",
                    ("新入社員", "開発部", 400000))
        conn.commit()
        print("トランザクション完了")
    except Exception as e:
        conn.rollback()
        print(f"ロールバック: {e}")
    finally:
        conn.close()

上級レベル:高度なデータベース操作

SQLAlchemyを使ったORM

from sqlalchemy import create_engine, Column, Integer, String
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker

Base = declarative_base()

class Employee(Base):
    __tablename__ = 'employees_orm'
    
    id = Column(Integer, primary_key=True)
    name = Column(String(100))
    department = Column(String(50))
    salary = Column(Integer)

# データベース接続
engine = create_engine('sqlite:///orm_example.db')
Base.metadata.create_all(engine)
Session = sessionmaker(bind=engine)

def orm_operations():
    session = Session()
    
    # データ追加
    new_employee = Employee(name="山田", department="営業部", salary=480000)
    session.add(new_employee)
    
    # データ検索
    employees = session.query(Employee).filter(Employee.department == "営業部").all()
    
    session.commit()
    session.close()

pandasとの連携

import pandas as pd

def pandas_integration():
    conn = sqlite3.connect('company.db')
    
    # SQLクエリ結果をDataFrameに読み込み
    df = pd.read_sql_query("""
        SELECT department, AVG(salary) as avg_salary, COUNT(*) as count
        FROM employees
        GROUP BY department
    """, conn)
    
    # DataFrameをSQLテーブルに保存
    sample_data = pd.DataFrame({
        'product': ['商品A', '商品B', '商品C'],
        'price': [1000, 1500, 2000]
    })
    sample_data.to_sql('products', conn, if_exists='replace', index=False)
    
    conn.close()
    return df

動的SQLクエリの構築

def build_dynamic_query(filters=None, sort_by=None):
    base_query = "SELECT * FROM employees WHERE 1=1"
    params = []
    
    if filters:
        if 'department' in filters:
            base_query += " AND department = ?"
            params.append(filters['department'])
        if 'min_salary' in filters:
            base_query += " AND salary >= ?"
            params.append(filters['min_salary'])
    
    if sort_by:
        base_query += f" ORDER BY {sort_by}"
    
    return base_query, params

def flexible_search(department=None, min_salary=None):
    conn = sqlite3.connect('company.db')
    cursor = conn.cursor()
    
    filters = {}
    if department: filters['department'] = department
    if min_salary: filters['min_salary'] = min_salary
    
    query, params = build_dynamic_query(filters, "salary DESC")
    cursor.execute(query, params)
    results = cursor.fetchall()
    
    conn.close()
    return results

SQL関連のエラーと解決方法

sqlite3.OperationalError:SQL構文エラー

def handle_sql_errors():
    conn = sqlite3.connect('company.db')
    cursor = conn.cursor()
    
    try:
        # 間違ったテーブル名
        cursor.execute("SELECT * FROM employee")  # employeesが正しい
    except sqlite3.OperationalError as e:
        print(f"SQLエラー: {e}")
    
    # SQLインジェクション対策
    def safe_query(user_input):
        # 危険:cursor.execute(f"SELECT * FROM employees WHERE name = '{user_input}'")
        # 安全:パラメータ化クエリ
        cursor.execute("SELECT * FROM employees WHERE name = ?", (user_input,))
        return cursor.fetchall()
    
    conn.close()

sqlite3.IntegrityError:制約違反エラー

def handle_integrity_errors():
    conn = sqlite3.connect('company.db')
    cursor = conn.cursor()
    
    # UNIQUE制約テーブル
    cursor.execute('''
        CREATE TABLE IF NOT EXISTS unique_employees (
            email TEXT UNIQUE NOT NULL,
            name TEXT NOT NULL
        )
    ''')
    
    try:
        cursor.execute("INSERT INTO unique_employees VALUES (?, ?)",
                      ("[email protected]", "テストユーザー"))
        # 重複エラー
        cursor.execute("INSERT INTO unique_employees VALUES (?, ?)",
                      ("[email protected]", "別のユーザー"))
    except sqlite3.IntegrityError as e:
        print(f"制約違反: {e}")
        # UPSERT操作
        cursor.execute("INSERT OR REPLACE INTO unique_employees VALUES (?, ?)",
                      ("[email protected]", "更新されたユーザー"))
    
    conn.commit()
    conn.close()

接続エラーとリソース管理

class DatabaseManager:
    def __init__(self, db_path):
        self.db_path = db_path
        self.conn = None
    
    def __enter__(self):
        self.conn = sqlite3.connect(self.db_path)
        return self.conn
    
    def __exit__(self, exc_type, exc_val, exc_tb):
        if self.conn:
            if exc_type is None:
                self.conn.commit()
            else:
                self.conn.rollback()
            self.conn.close()

# 使用例
def safe_operation():
    with DatabaseManager('company.db') as conn:
        cursor = conn.cursor()
        cursor.execute("SELECT COUNT(*) FROM employees")
        count = cursor.fetchone()[0]
        print(f"従業員数: {count}")

パフォーマンス最適化

インデックスの活用

def create_indexes():
    conn = sqlite3.connect('company.db')
    cursor = conn.cursor()
    
    # 単一カラムインデックス
    cursor.execute("CREATE INDEX IF NOT EXISTS idx_department ON employees(department)")
    
    # 複合インデックス
    cursor.execute("CREATE INDEX IF NOT EXISTS idx_dept_salary ON employees(department, salary)")
    
    conn.commit()
    conn.close()

バッチ処理

def batch_operations():
    conn = sqlite3.connect('company.db')
    cursor = conn.cursor()
    
    # 効率的なバッチ挿入
    batch_data = [(f"社員{i}", "営業部", 400000) for i in range(1000)]
    cursor.executemany(
        "INSERT INTO employees (name, department, salary) VALUES (?, ?, ?)",
        batch_data
    )
    
    conn.commit()
    conn.close()

実践的なSQL活用パターン

ログ分析システム

def create_log_analysis():
    conn = sqlite3.connect('logs.db')
    cursor = conn.cursor()
    
    # ログテーブル作成
    cursor.execute('''
        CREATE TABLE IF NOT EXISTS access_logs (
            timestamp DATETIME,
            ip_address TEXT,
            url TEXT,
            status_code INTEGER
        )
    ''')
    
    # エラー率分析
    cursor.execute('''
        SELECT url, COUNT(*) as total,
               SUM(CASE WHEN status_code >= 400 THEN 1 ELSE 0 END) as errors,
               ROUND(SUM(CASE WHEN status_code >= 400 THEN 1 ELSE 0 END) * 100.0 / COUNT(*), 2) as error_rate
        FROM access_logs
        GROUP BY url
        ORDER BY error_rate DESC
    ''')
    
    conn.commit()
    conn.close()

売上レポート

def sales_reporting():
    conn = sqlite3.connect('sales.db')
    cursor = conn.cursor()
    
    # 月次売上レポート
    cursor.execute('''
        SELECT strftime('%Y-%m', sale_date) as month,
               SUM(quantity * unit_price) as total_sales
        FROM sales
        GROUP BY strftime('%Y-%m', sale_date)
        ORDER BY month DESC
    ''')
    monthly_sales = cursor.fetchall()
    
    # 商品別売上ランキング
    cursor.execute('''
        SELECT product_name, SUM(quantity * unit_price) as revenue
        FROM sales
        GROUP BY product_name
        ORDER BY revenue DESC
        LIMIT 10
    ''')
    top_products = cursor.fetchall()
    
    conn.close()
    return monthly_sales, top_products

在庫管理システム

def inventory_management():
    conn = sqlite3.connect('inventory.db')
    cursor = conn.cursor()
    
    # 在庫切れ警告
    cursor.execute('''
        SELECT product_name, current_stock, min_stock
        FROM inventory
        WHERE current_stock <= min_stock
        ORDER BY (min_stock - current_stock) DESC
    ''')
    low_stock = cursor.fetchall()
    
    # 在庫回転率
    cursor.execute('''
        SELECT product_name, 
               current_stock,
               COALESCE(total_sold / current_stock, 0) as turnover_ratio
        FROM inventory
        LEFT JOIN (
            SELECT product_id, SUM(quantity) as total_sold
            FROM sales
            WHERE sale_date >= date('now', '-30 days')
            GROUP BY product_id
        ) sales_data ON inventory.product_id = sales_data.product_id
        ORDER BY turnover_ratio DESC
    ''')
    turnover = cursor.fetchall()
    
    conn.close()
    return low_stock, turnover

ベストプラクティス

セキュリティ対策

def security_practices():
    # 1. パラメータ化クエリの使用
    def safe_search(name, department):
        conn = sqlite3.connect('company.db')
        cursor = conn.cursor()
        cursor.execute(
            "SELECT * FROM employees WHERE name = ? AND department = ?",
            (name, department)
        )
        return cursor.fetchall()
    
    # 2. 入力値検証
    def validate_input(name, salary):
        if not name or len(name.strip()) == 0:
            raise ValueError("名前は必須です")
        if not isinstance(salary, (int, float)) or salary < 0:
            raise ValueError("給与は正の数値である必要があります")
        return True

エラーハンドリング

class DatabaseManager:
    def __init__(self, db_path):
        self.db_path = db_path
    
    def execute_query(self, query, params=None):
        try:
            conn = sqlite3.connect(self.db_path)
            cursor = conn.cursor()
            
            if params:
                cursor.execute(query, params)
            else:
                cursor.execute(query)
            
            result = cursor.fetchall()
            conn.commit()
            return result
            
        except sqlite3.Error as e:
            if conn:
                conn.rollback()
            raise Exception(f"データベースエラー: {e}")
        finally:
            if conn:
                conn.close()

パフォーマンス監視

import time
from functools import wraps

def monitor_query(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        start_time = time.time()
        result = func(*args, **kwargs)
        execution_time = time.time() - start_time
        
        if execution_time > 1.0:  # 1秒以上
            print(f"遅いクエリ検出: {func.__name__} - {execution_time:.2f}秒")
        
        return result
    return wrapper

@monitor_query
def slow_query_example():
    conn = sqlite3.connect('company.db')
    cursor = conn.cursor()
    cursor.execute("SELECT * FROM employees ORDER BY salary")
    return cursor.fetchall()

まとめ

PythonとSQLの組み合わせは、データ駆動型アプリケーションの開発において強力な武器となります。基本的なCRUD操作から高度な分析クエリ、パフォーマンス最適化まで、段階的に理解を深めることが重要です。

重要なポイント:

  • セキュリティ:パラメータ化クエリによるSQLインジェクション対策
  • パフォーマンス:インデックスとバッチ処理による最適化
  • 保守性:適切なエラーハンドリングとトランザクション管理
  • スケーラビリティ:接続プールと監視システムの導入
  • 可読性:動的クエリ構築とORMの適切な使用

実際のプロジェクトでは、データの性質とアプリケーションの要件に応じて、最適な技術選択を行うことが成功の鍵となります。SQLiteから始めて、必要に応じてより高機能なデータベースシステムへの移行を検討してください。

「らくらくPython塾」が切り開く「呪文コーディング」とは?

■プロンプトだけでオリジナルアプリを開発・公開してみた!!

■AI時代の第一歩!「AI駆動開発コース」はじめました!

テックジム東京本校で先行開始。

■テックジム東京本校

「武田塾」のプログラミング版といえば「テックジム」。
講義動画なし、教科書なし。「進捗管理とコーチング」で効率学習。
より早く、より安く、しかも対面型のプログラミングスクールです。

<短期講習>5日で5万円の「Pythonミニキャンプ」開催中。

<月1開催>放送作家による映像ディレクター養成講座

<オンライン無料>ゼロから始めるPython爆速講座

フリーランスボード

20万件以上の案件から、副業に最適なリモート・週3〜の案件を一括検索できるプラットフォーム。プロフィール登録でAIスカウトが自動的にマッチング案件を提案。市場統計や単価相場、エージェントの口コミも無料で閲覧可能なため、本業を続けながら効率的に高単価の副業案件を探せます。フリーランスボード

ITプロパートナーズ

週2〜3日から働ける柔軟な案件が業界トップクラスの豊富さを誇るフリーランスエージェント。エンド直契約のため高単価で、週3日稼働でも十分な報酬を得られます。リモートや時間フレキシブルな案件も多数。スタートアップ・ベンチャー中心で、トレンド技術を使った魅力的な案件が揃っています。専属エージェントが案件紹介から契約交渉までサポート。利用企業2,000社以上の実績。ITプロパートナーズ

Midworks 10,000件以上の案件を保有し、週3日〜・フルリモートなど柔軟な働き方に対応。高単価案件が豊富で、報酬保障制度(60%)や保険料負担(50%)など正社員並みの手厚い福利厚生が特徴。通勤交通費(月3万円)、スキルアップ費用(月1万円)の支給に加え、リロクラブ・freeeが無料利用可能。非公開案件80%以上、支払いサイト20日で安心して稼働できます。Midworks