【Python】MySQL操作の基本:mysqlclientでの接続設定とIN句を使ったデータ取得

PythonからMySQLデータベースを操作する際、最も標準的なドライバである mysqlclient(インポート名は MySQLdb)の使用方法を解説します。接続情報の管理から、リスト型のデータを条件に含める高度なSQL実行方法まで、実務で使えるコードを紹介します。

目次

接続パラメータの意味と設定

データベースへの接続時、connect() 関数に渡す主要な引数は以下の通りです。

パラメータ意味設定内容の例
userユーザー名データベースにログインするためのユーザー名(例: admin
passwdパスワード上記ユーザーに対応するパスワード
hostホスト名MySQLサーバーのIPアドレスまたはドメイン(ローカルなら localhost
dbデータベース名接続先となるデータベース(スキーマ)の名前
portポート番号接続ポート(MySQLのデフォルトは整数 3306
charset文字コード日本語を扱う場合は utf8mb4 を推奨

ライブラリのインストール

Python環境で以下のコマンドを実行し、ライブラリをインストールします。

pip install mysqlclient

実践サンプルコード:記事データの検索

ここでは、ブログシステムを想定し、「指定したID以降」かつ「特定のカテゴリーIDリストに含まれる」記事データを取得する処理を実装します。

特に、SQLの IN 句に対してPythonのリスト(配列)を渡す場合、単一の %s では処理できないため、リストの長さに応じてプレースホルダを動的に生成するテクニックが必要です。

import MySQLdb
import os

# 接続情報を辞書で定義
# 実際の運用では環境変数から読み込むことが推奨されます
DB_CONFIG = {
    "user": "blog_admin",
    "passwd": "secure_password_here",
    "host": "localhost",
    "db": "cms_database",
    "port": 3306,
    "charset": "utf8mb4"
}

def main():
    # 検索条件
    # IDが100より大きく、かつカテゴリーIDが [1, 5, 9] のいずれかである記事を探す
    min_post_id = 100
    target_category_ids = [1, 5, 9]

    # 【重要】IN句用のプレースホルダを動的に生成
    # リストの要素数が3の場合、"%s, %s, %s" という文字列を作成します
    format_strings = ', '.join(['%s'] * len(target_category_ids))

    # SQL文の構築
    # 安全のため、値は直接埋め込まずプレースホルダを使用します
    sql = f"""
        SELECT 
            id, 
            title, 
            category_id, 
            published_at 
        FROM 
            blog_posts 
        WHERE 
            id > %s 
            AND category_id IN ({format_strings})
    """

    # パラメータの準備
    # ID条件とカテゴリーIDリストを1つのタプルまたはリストに結合します
    # executeメソッドにはフラットな(ネストしていない)引数を渡す必要があります
    params = [min_post_id] + target_category_ids

    print(f"Executing Query: {sql}")
    print(f"Parameters: {params}")

    try:
        # データベース接続
        # **DB_CONFIG で辞書を展開して引数として渡しています
        with MySQLdb.connect(**DB_CONFIG) as conn:
            
            # カーソルの取得(DictCursorを使うと辞書形式で取得可能ですが、ここは標準のタプル形式)
            with conn.cursor() as cur:
                
                # SQLの実行
                cur.execute(sql, params)
                
                # 結果の取得
                rows = cur.fetchall()
                
                # 結果の表示
                print(f"\nFound {len(rows)} articles:")
                print("-" * 30)
                for row in rows:
                    # rowはタプル (id, title, category_id, published_at)
                    print(f"ID: {row[0]} | Title: {row[1]} | Date: {row[3]}")

    except MySQLdb.Error as e:
        print(f"Database Error occurred: {e}")
    except Exception as e:
        print(f"An unexpected error occurred: {e}")

if __name__ == "__main__":
    main()

コードの詳細解説

1. 接続情報の辞書展開 (**con_info)

MySQLdb.connect(user="root", passwd="...", ...) と引数を羅列する代わりに、辞書 DB_CONFIG を定義し、呼び出し時に **DB_CONFIG と記述することで、辞書のキーと値を引数として展開して渡しています。これにより設定とロジックが分離され、コードが見やすくなります。

2. with ステートメントと接続管理

with MySQLdb.connect(...) as conn: ブロックを使用することで、処理がブロックを抜けた際(またはエラー発生時)に適切な終了処理が行われます。ただし、明示的なトランザクション管理(commit / rollback)が必要な更新処理では、意図を明確にするために手動で記述することもあります。

3. IN 句のプレースホルダ生成

最も注意すべき点は AND category_id IN (%s) という記述では、リスト [1, 5, 9] を正しく処理できない点です。MySQLdbはリスト全体を1つの文字列として扱ってしまうため、SQL構文エラーや意図しない検索結果になります。

正しい手順は以下の通りです:

  1. リストの要素数分だけ %s を用意する(例:%s, %s, %s)。
  2. SQL文の中にその文字列を埋め込む。
  3. execute メソッドの第2引数には、リストを展開してフラットな状態で渡す。

4. SQLインジェクション対策

検索条件の数値をSQL文字列に直接 f"id > {min_post_id}" のように埋め込むのはセキュリティリスクがあります。必ず %s プレースホルダを使用し、execute メソッドの第2引数として値を渡すことで、ドライバ側で安全にエスケープ処理を行わせます。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

私が勉強したこと、実践したこと、してることを書いているブログです。
主に資産運用について書いていたのですが、
最近はプログラミングに興味があるので、今はそればっかりです。

目次