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構文エラーや意図しない検索結果になります。
正しい手順は以下の通りです:
- リストの要素数分だけ
%sを用意する(例:%s, %s, %s)。 - SQL文の中にその文字列を埋め込む。
executeメソッドの第2引数には、リストを展開してフラットな状態で渡す。
4. SQLインジェクション対策
検索条件の数値をSQL文字列に直接 f"id > {min_post_id}" のように埋め込むのはセキュリティリスクがあります。必ず %s プレースホルダを使用し、execute メソッドの第2引数として値を渡すことで、ドライバ側で安全にエスケープ処理を行わせます。
