WEBのシステム開発はPHPを使用していますが、データ解析等のため Python3 の研究を始めました。Python3 は日本語の情報がとても少ないので、エンジニアや学生の皆さんに役立ちそうなことをブログ記事として共有できればと思います。
この記事では Python3 で MySQL に接続する方法、エラー処理(例外処理)、サンプルによるCRUD(※)、PHPer向け注意点についてまとめました。なおサンプルは Django 等、特定フレームワークの ORM ではなく、SQLの直接実行例です。
SQLite3 は Python3でSQLite3を使う – 基本操作からエラー処理までサンプルコード付を参照ください。
目次
Python3 で MySQL を使うための準備
Python3 には MySQL を操作する標準モジュールはありません。MySQL を操作するために、まず mysqlclient をインストールしましょう。
1 2 3 |
$ pip install mysqlclient # または $ pip3 install mysqlclient |
関連する公式ページ
https://pypi.python.org/pypi/mysqlclient/
https://mysqlclient.readthedocs.io/
スクリプトからも操作できますが、あらかじめ MySQL でデータベース sample_db を作っておきましょう。
1 2 3 |
mysql> CREATE DATABASE sample_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; |
では CRUD の操作をみていきます。
接続 / CREATE / INSERT
接続からテーブル作成・データ挿入まで、よくある操作をサンプルコードにまとめてみました。
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 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
# Python 3.5.2 にて動作を確認 # MySQLdb をインポート import MySQLdb # データベース接続とカーソル生成 # 接続情報はダミーです。お手元の環境にあわせてください。 connection = MySQLdb.connect( host='localhost', user='root', passwd='yourpass', db='sample_db', charset='utf8') cursor = connection.cursor() # エラー処理(例外処理) try: # CREATE # id, name だけのシンプルなテーブルを作成。id を主キーに設定。 cursor.execute("DROP TABLE IF EXISTS `sample`") cursor.execute("""CREATE TABLE IF NOT EXISTS `sample` ( `id` int(11) NOT NULL, `name` varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL, PRIMARY KEY (id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci""") # INSERT cursor.execute("INSERT INTO sample VALUES (1, '佐藤')") # プレースホルダの使用例 # 1つの場合には最後に , がないとエラー。('鈴木') ではなく ('鈴木',) cursor.execute("INSERT INTO sample VALUES (2, %s)", ('鈴木',)) cursor.execute("INSERT INTO sample VALUES (%s, %s)", (3, '高橋')) cursor.execute("INSERT INTO sample VALUES (%(id)s, %(name)s)", {'id': 4, 'name': '田中'}) # 複数レコードを一度に挿入 executemany メソッドを使用 persons = [ (5, '伊藤'), (6, '渡辺'), ] cursor.executemany("INSERT INTO sample VALUES (%s, %s)", persons) # わざと主キー重複エラーを起こして例外を発生させてみる cursor.execute("INSERT INTO sample VALUES (1, '中村')") except MySQLdb.Error as e: print('MySQLdb.Error: ', e) # 保存を実行(忘れると保存されないので注意) connection.commit() # 接続を閉じる connection.close() |
ポイント解説
エラー処理
MySQL の操作上のエラーは MySQLdb.Error により処理できます。データベースの操作に予期せぬエラーはつきものなので、必ず例外処理をしておきましょう。上のサンプルソースでもわざと不正なSQLを実行して例外を発生させています。
プレースホルダ
プレースホルダには ? (疑問符)が使われることが多いですが MySQLdb では %s を用います。名前つきプレースホルダも %(hoge)s といったように利用できるようです(上のサンプルコードを参照)。
PHPer 向け注意事項
PHP の PDO は INSERT / UPDATE / DELETE 文の実行と同時にデータの更新が行われますが、Python3 + MySQLdb では(上のサンプルコードの最終行のように)必ずコミットをする必要があります。connection.commit() を忘れるとデータが保存されないことに注意しましょう。なお MySQLdb に限ったことではなく、Python3標準モジュールのSQLite3でも同様です。
SELECT
SELECT文を使ってよくやることをまとめてみました。件数取得、全件データ取得、1件データ取得、WHERE句など大抵のことは下記サンプルでわかるかと思います。
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 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
# SELECT cursor.execute('SELECT * FROM sample ORDER BY id ASC') # 件数の取得は Cusor オブジェクトの rowcount プロパティー print(cursor.rowcount) # 6 # 全件取得は cursor.fetchall() print(cursor.fetchall()) # ((1, '佐藤'), (2, '鈴木'), (3, '高橋'), (4, '田中'), (5, '伊藤'), (6, '渡辺')) # 1件取得 cursor.fetchone() # fetchone() メソッド実行のたびにカーソル位置が移動します cursor.execute('SELECT * FROM sample ORDER BY id') print(cursor.fetchone()) # (1, '佐藤') print(cursor.fetchone()) # (2, '鈴木') print(cursor.fetchone()) # (3, '高橋') # WHERE 句の例 cursor.execute('SELECT * FROM sample WHERE name=%s', ('佐藤',)) print(cursor.rowcount) # 1 print(cursor.fetchone()) # (1, '佐藤') # WHERE 句で該当なしの場合 cursor.execute('SELECT * FROM sample WHERE name=%s', ('SUZUKI',)) print(cursor.rowcount) # 0 print(cursor.fetchall()) # () print(cursor.fetchone()) # None # 全件ループ表示方法は2パターン。いずれも同じ結果が得られます。 cursor.execute('SELECT * FROM sample ORDER BY id ASC') for row in cursor: print(row) cursor.execute('SELECT * FROM sample ORDER BY id ASC') for row in cursor.fetchall(): print(row) # いずれも下記の出力結果となります。 """ (1, '佐藤') (2, '鈴木') (2, '鈴木') (4, '田中') (5, '伊藤') (6, '渡辺') """ |
ポイント解説
全件ループ表示は上のように2パターンありますので、どちらも覚えておくのがよいです。パフォーマンスはどちらも違いはないでしょう(?)。cursor.fetchall() の方は全件取得してループするのだとわかりますが、for row in cursor: でなぜループ表示できるのか?不思議に思うかもしれません。(内部的なことは知らなくてもよいのですが)イテレータという仕組みがあるからこのようにシンプルに書けるのです。
PHPer 向け – 辞書型(連想配列)で取得するには?
PHP の PDO の場合、結果セットは $row[‘name’] のように連想配列で受け取れますが、cursor.fetchone() , cursor.fetchall() ともに結果セットは 0 で始まる単純な配列(正確にはタプルというデータ型)になっています。PDO::FETCH_NUM と同じ挙動です。
row[0], row[1] ではなく、row[‘id’], row[‘name’] のように結果セットを連想配列(Python では辞書型と呼びます)で受け取りたいケースも多いかと思います。カーソルオブジェクト生成の時に MySQLdb.cursors.DictCursor を引数に指定してみましょう。
1 2 3 |
connection = MySQLdb.connect( host='localhost', user='root', passwd='testpass', db='sample_db', charset='utf8') cursor = connection.cursor(MySQLdb.cursors.DictCursor) |
MySQLdb.cursors.DictCursor を指定したカーソルは結果セットを辞書型で返すため、row[‘name’] のように扱え、PHP PDO の PDO::FETCH_ASSOC と同様になります。
参考: http://php.net/manual/ja/pdostatement.fetch.php
UPDATE / DELETE
UPDATE と DELETE には特別なことはありません。上の INSERT と同様にコミットしないと変更が保存されないこと、適宜 MySQLdb.Error で例外処理をすることを忘れないようにしましょう。
1 2 3 4 5 6 7 8 |
# UPDATE cursor.execute('UPDATE sample SET name=%s WHERE id=1', ('小林',)) cursor.execute('UPDATE sample SET name=%s WHERE id=%s', ('加藤', 2)) connection.commit() # DELETE cursor.execute('DELETE FROM sample WHERE id > 3') connection.commit() |
以上、Python3 で MySQL を使う方法をまとめてみました。弊社の専門はPHP言語なので、おかしなところがあればご指摘ください。
※この記事の内容は適宜更新することがあります。