【python】MySQLを操作する
pythonからMySQLを操作する
MySQLサーバーの構築ができたので、Pythonから操作していきます。
ライブラリのインストール
pythonからMySQLを操作するにあたって、ライブラリのインストールを行います。
ChatGPTに聞いたところ、以下の2つのライブラリが提案されました。
・mysql-connector-python
・PyMySQL
私はpyにどっぷり染まっているので、迷わず後者のpymysqlを選択しました。
本当はメリット・デメリットを検証した方がいいんでしょうけどね。
インストール方法は言わずもがな。pipコマンドのみです。
pip install PyMySQL
データベースへ接続する
データベースへ接続を行います。
PyMySQLではConnectオブジェクトを使います。
hostからcharsetまでは、MySQLサーバー構築時に設定したものです。
# MySQLに接続(local_infile=True がポイント)
self.connection: pymysql.Connection[Cursor] = pymysql.connect(
host=hostname,
user=user_id,
password=password,
database=database,
charset=self.CHAR_SET,
local_infile=self.LOCAL_INFILE
)
local_infileは定数化していますがTrueを指定しています。
LOCAL_INFILE:bool = True
これは、クライアント側のローカルファイルを使ってMySQLサーバーへデータ投入することができる指定です。(ちょっと理解が間違えているかも)
この定義はサーバー側、クライアント側ともにONになっている必要があります。
クライアント側はプログラム側でTrueにすればOK
サーバー側は「/etc/my.cnf」に対して設定を追加します。
これはあくまでDocker on MySQLの場合で、環境によって異なる場合がありますのでご注意ください。
[mysqld]
local_infile=1
この定義を追加します。
なお、私のDocker環境にはviエディター、nanoエディターもろもろが入っていないため、echoコマンドを使って追記しようとしたところ誤って上書きしてしまったので、mac側でmy.cnfファイルを作成して、scpコマンドでアップロードし直しました。(事前にcatしておいてよかった...)
定義追加後、DockerのMySQLサーバーを再起動します。
docker restart コンテナID または コンテナ名
その後、MySQLサーバーへ入り直し、以下コマンドで結果を確認します。
mysql> SHOW GLOBAL VARIABLES LIKE 'local_infile';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| local_infile | ON |
+---------------+-------+
1 row in set (0.00 sec)
local_infile の Value が ON になっていれば OK です。
最初、定義を追加したのに OFF から ON にならず、設定ファイルが実は違うところにあるのかと疑ったのですが、macで再作成するときに拡張子を間違えて「my.conf」でファイルを作っていたためでした。
mvコマンドで 「my.cnf」にリネームしたら無事反映されました。
テーブルへデータをインポートする
まず、今回やりたかったのはデータのインポートでしたので、インポート用のSQLを作ります。
PyMySQLではConnectオブジェクトを使います。
hostからcharsetまでは、MySQLサーバー構築時に設定したものです。
sql = f"""
LOAD DATA LOCAL INFILE '{csv_path}'
INTO TABLE {tablename}
FIELDS TERMINATED BY ','
ENCLOSED BY '"'
LINES TERMINATED BY '{rtnchar}'
"""
LOCAL INFILE はプログラムで作成するCSVファイルの絶対パスです。
先ほどのlocal_infileが OFF だと以下のようなエラーが出ます。
"/Users/ユーザー名/.pyenv/versions/3.13.2/lib/python3.13/site-packages/pymysql/err.py", line 150, in raise_mysql_exception raise errorclass(errno, errval) pymysql.err.OperationalError: (3948, 'Loading local data is disabled; this must be enabled on both the client and server sides')
INTO TABLE はテーブル名です。
FIELDS TERMINATED BY は区切り文字です。今回はCSVファイルなので,(カンマ)です。
ENCLOSED BY ;は項目を囲っている記号です。今回はダブルクォーテーションです。(インポートが終わったら中間ファイルを速攻で消す動きになっているので、ここが実際に合っているかをみれていないw)
LINES TERMINATED BY は改行コードです。環境に合わせて設定してください。
続いてSQLの実行です。
コネクションからカーソルを作成します。
カーソル(cursor) とは、SQLを実行したり、結果を取り出すための「操作用オブジェクト」 のことです。(by ChatGPT)
# カーソルを生成する
myCorsor = self.connection.cursor()
カーソルを作ったらSQLを実行させます。
# SQLを実行する
myCorsor.execute(sql)
実行が終わったらコミットします。(結果にコミ。。。)
# コミットする
self.connection.commit()
コミットまで終了したらカーソルを閉じておきます。
基本的には使い終わったら閉じておくのがよいかと思いますが、プログラム次第ですのでご自由に、ですね。
# カーソルを閉じる
myCorsor.close()
mysql> select * from NVD_DB.NVD_CVE_INFO;
+-------+------------------+---------------------+---------------------+-------------------------+
| cveid | sourceidentifier | published | lastModified | vulnStatus |
+-------+------------------+---------------------+---------------------+-------------------------+
| | 0 | 0000-00-00 00:00:00 | 0000-00-00 00:00:00 | 3 |
| 0 | CVE-1999-0095 | 0000-00-00 00:00:00 | 1988-10-01 04:00:00 | 2025-04-03T01:03:51.193 |
+-------+------------------+---------------------+---------------------+-------------------------+
妙なゴミデータが入っていますが、インポートできるはできています。
あれ、、、なんかインポート結果がおかしいw
こんな感じでインポートは終了です。
コネクションを切断する
後始末のdisconnectです。
self.connection.close()
コネクションを貼ったら、オブジェクトのプロパティに一時的に持たせているので selfがついていますが、close()メソッドを発行するだけです。
コネクションも基本的には使い割ったら閉じる方がよいと思います。
用もないのにプログラム実行中コネクションを貼り続けるのもどうかと思うので、必要なときに必要な間ただけコネクトおくのがよいと思います。
今回はここまでです。また、MySQL関連で気づきがあれば追記していきます。
コメント
コメントを投稿