Pythonはつい最近始めたばかりです。
だからこそ新しい学びが多いですね。
今回はMySQLクエリの動的な作り方について
端的に書くとこのような状況です
- mysql.connectorをMySQL操作に使用
- WHERE IN句に配列を渡したい
- プリペアードステートメントを使って
こういう場面は多いかもしれない
その方法がようやく理解できたので、
同じような人のために記録に残しておきます。
このページの目次
WHERE IN句に配列(list)を展開して渡したかった
例えばこのようなテーブルがあるとします。
▼ 説明用にusersテーブルを作る
1 2 3 4 5 |
CREATE TABLE users ( user_id INTEGER AUTO_INCREMENT, name VARCHAR(30), PRIMARY KEY (user_id) ) |
▼ 適当にデータを挿入する
1 2 3 4 |
INSERT INTO users (name) VALUES ('Tom'), ('Mike') , ('Luna'), ('Olivia'), ('John'), ('Emma'); |
▼ こういうデータができる
user_id | name |
---|---|
1 | Tom |
2 | Mike |
3 | Luna |
4 | Olivia |
5 | John |
6 | Emma |
普通にテーブルを作ってレコード挿入しただけ。
ここまでは問題ないです。
問題はmysql.connectorからSQLを実行した時
問題はPythonのmysql.connetorでコードを書いたときです。
▼ 例えばこういうコードの場合
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
import mysql.connector as mydb ## DB接続 conn = mydb.connect( host='localhost', port='3306', user='root', password='mypassword', database='mydatabase' ) ## 取得したいユーザーID配列 user_ids = ['1','3','6'] ## SQLインジェクションを意識しない場合、 ## 無理やりこういうコードも書けるが... cur = conn.cursor() stmt = """ SELECT * FROM users WHERE user_id IN ( """ + ','.join(user_ids) + ")" cur.execute(stmt) ## クエリ結果の取得処理... cur.close() |
もし user_ids がコード的に固定であったり、外部入力を受け取らないなら許容できるかもしれません。それなら変な値が入り込む余地は少ないからです。
でも可変であるならSQLインジェクションの対象になります。外部からデータを受け取って動的にクエリを組み立てているなら尚更ですね。
基本はプリペアードステートメントを使うべき
mysql.connectorでWHERE IN句にlistを直接渡すコード例
では具体的にlistを展開して渡す方法について
先ほどの例だと次のコードで実現できます。
▼ listを動的にクエリに渡すコード例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
## ユーザーID配列 user_ids = ['1','3','6'] cur = conn.cursor() ## プレースホルダを動的作成 stmt_formats = ','.join(['%s'] * len(user_ids)) stmt = """ SELECT * FROM users WHERE user_id IN (%s) """ ## プリペアードステートメント実行 cur.execute( stmt % stmt_formats, tuple(user_ids) ) ## クエリ結果の取得... cur.close() |
まず ','.join(['%s'] * len(user_ids)) によって配列の長さ分だけプレースホルダを用意します。この例だと '%s,%s,%s' みたいな文字列です。(ユーザーIDの個数が3つの場合)
あとはプリペアードステートメントを実行する際に stmt % stmt_formats として実行クエリ内の %s を stmt_formats に置換し、さらにタプル化したユーザーIDを変数として渡してるだけです。
ちょっと説明が難しい…
でもコードを辿れば理解できるはずです。
ここで紹介した方法を使う方が安全なのは間違いない
SQLインジェクションは可変値でのクエリ組み立てで起こります。
mysql.connectorでもプリペアードステートメントは使えるけど…
こういう特殊な場面でも少しだけ工夫が必要ですね。
あと参考として次のPython・MySQL関連記事も紹介します。
▼ Pythonでダウンロードなしでファイルサイズを調べる方法
▼ WHERE IN に指定した順番でレコード取得する方法
以上、Pyhtonのmysqlクエリに配列を渡す方法でした。
もし間違いがあれば教えてください。ではまた (@^^)/~~~