今回の記事では、PythonのコードでSQLを実行しAuroraを操作する方法を紹介します。
AuroraはMySQL互換のインスタンスを使っているので、PythonのライブラリはMySQL用のものを利用します。
複数種類あるのですが、僕はPyMySQLを利用することが多いです。
(職場で先輩が使ってたというだけの理由で使い続けており、他のライブラリと比較検証したわけじゃないので、
いつかちゃんとメリットデメリット比較したいです。)
PyMySQL のドキュメントはこちら
PyMySQL · PyPI
前回の記事でも書きましたが、サンプルコードが軽く記載してあるだけで、あまり詳しい説明などはありません。
pep249を必要に応じて参照する必要があります。
といっても、使い方は簡単なので、ドキュメントのサンプルコードだけあれば実用上はなんとか使っていけます。
(DBヘの接続、SQLの実行と結果の取り出しができれば十分。)
この記事では、サンプルコードを参考に、最低限の操作をやってみようと思います。
まず、DBにサンプルのTableを作っておきます。
> CREATE TABLE users (
id int(11) NOT NULL AUTO_INCREMENT,
email varchar(255) NOT NULL,
password varchar(255) NOT NULL,
PRIMARY KEY (`id`)
);
Query OK, 0 rows affected (0.05 sec)
まず、DBへの接続です。
DBのエンドポイント/ユーザー名/パスワード/DB名 をそれぞれ設定し、接続をします。
cursorclass=pymysql.cursors.DictCursor
を設定するのは、結果をDict形で受け取るためです。
(これは必須ではないのですが、Dictで受け取った方が後々扱いやすいです。)
{}で書いてる部分は各自の環境の値を入れてください。
import pymysql.cursors
con_args = {
"host": "{RDSのエンドポイント/同サーバーのDB場合はlocalhost}",
"user": "{ユーザー名}",
"password": "{パスワード}",
"database": "{DB名}",
"cursorclass": pymysql.cursors.DictCursor
}
connection = pymysql.connect(**con_args)
新規レコードを挿入してみます。
SQLインジェクション対策にプレースホルダ(%s)を使います。
自動コミットではないので、 connection.commit()
を忘れないようにするのがポイントです。
with connection.cursor() as cursor:
sql = "INSERT INTO `users` (`email`, `password`) VALUES (%s, %s)"
cursor.execute(sql, ('webmaster@python.org', 'very-secret'))
connection.commit()
次はSELECT文です。 1レコード取り出すサンプルコードは次のようになります。
with connection.cursor() as cursor:
sql = "SELECT `id`, `password` FROM `users` WHERE `email`=%s"
cursor.execute(sql, ('webmaster@python.org',))
result = cursor.fetchone()
print(result)
# 結果
# {'id': 1, 'password': 'very-secret'}
ドキュメントのサンプルコードはこれだけですね。
ほとんど代わり映えしないのですが、結果が複数行になるSELECT文も試しておきましょう。
まずレコード自体が1行しかないとどうしようもないのでデータを増やします。
with connection.cursor() as cursor:
sql = "INSERT INTO `users` (`email`, `password`) VALUES (%s, %s)"
cursor.execute(sql, ('sato@python.org', 'very-secret'))
cursor.execute(sql, ('suzuki@python.org', 'very-secret'))
cursor.execute(sql, ('takahashi@python.org', 'very-secret'))
cursor.execute(sql, ('tanaka@python.org', 'very-secret'))
connection.commit()
結果が複数行になる場合は、fetchone()
の代わりに、fetchall()
を使います。
with connection.cursor() as cursor:
sql = "SELECT id, email FROM users"
cursor.execute(sql)
result = cursor.fetchall()
print(result)
# [{'id': 1, 'email': 'webmaster@python.org'}, {'id': 2, 'email': 'sato@python.org'},
# {'id': 3, 'email': 'suzuki@python.org'}, {'id': 4, 'email': 'takahashi@python.org'},
# {'id': 5, 'email': 'tanaka@python.org'}]
ちなみにこの結果の型ですが、pandasのDataFrameに簡単に変換できるので便利です。
import pandas as pd
df = pd.DataFrame(result)
print(df)
"""
id email
0 1 webmaster@python.org
1 2 sato@python.org
2 3 suzuki@python.org
3 4 takahashi@python.org
4 5 tanaka@python.org
"""
もちろんfetchoneを繰り返して1行ずつ取り出すこともできるのですが、個人的にはfetchallでさっさと取り出して
データフレームにしてしまった方が扱いやすいと思います。
fetchoneで順番に取り出すとしたら次のようなコードでしょうか。
with connection.cursor() as cursor:
sql = "SELECT id, email FROM users"
cursor.execute(sql)
row = cursor.fetchone()
while row is not None:
print(row)
row = cursor.fetchone()
"""
{'id': 1, 'email': 'webmaster@python.org'}
{'id': 2, 'email': 'sato@python.org'}
{'id': 3, 'email': 'suzuki@python.org'}
{'id': 4, 'email': 'takahashi@python.org'}
{'id': 5, 'email': 'tanaka@python.org'}
"""
次のような書き方でも動きます。
with connection.cursor() as cursor:
sql = "SELECT id, email FROM users"
cursor.execute(sql)
for row in cursor:
print(row)
"""
{'id': 1, 'email': 'webmaster@python.org'}
{'id': 2, 'email': 'sato@python.org'}
{'id': 3, 'email': 'suzuki@python.org'}
{'id': 4, 'email': 'takahashi@python.org'}
{'id': 5, 'email': 'tanaka@python.org'}
"""