PythonでMySQLのデータ操作をやろう

普段はDjangoでO/Rマッパーの恩恵にどっぷり漬かってるため下手したら
SQL文すら書かないでできるデータ操作をDjangoとなにも関係ないテーブルに
対してやってみろと言われて"Sir, yes sir!"とできますか？私はできませんでした！
じゃあがんばってやってみたのでまとめるよ！なQiita記事始まります！

正直やることはどの言語でも大して変わらないので一個覚えたら他の言語でも応用は
きくと思います。

## MySQLDBコネクター
Djangoを使ってるならMySQLのコネクターぐらい入ってますよね？よし、次！
(どれがいいかは好みなので省略)
一応この記事はMySQL-python 1.2.5でのことです。

## 繋いでみよう
```
>>> import MySQLdb

>>>> conn = MySQLdb.connect(host='hoge_host', user='hoge_user', passwd='hogehoge', db='hoge1')
```
とりあえずコネクションを作ってみましょう！
MySQLdbにはconnection()とconnect()がありますが使うのはconnect()です。
なぜかと言うと、

```
>>> conn = MySQLdb.connection(host='hoge_host', user='hoge_user', passwd='hogehoge', db='hoge1')
>>> print type(conn)
<type '_mysql.connection'>
```

```
>>> conn = MySQLdb.connect(host='hoge_host', user='hoge_user', passwd='hogehoge', db='hoge1')
>>> print type(conn)
<class 'MySQLdb.connections.Connection'>
```

はい、わかりましたね？connection()で繋ぐと_mysql.connectionが、
connect()で繋ぐとMySQLdb.connections.Connectionオブジェクトが生成されました。

_mysqlクラスはMySQLdbの基底クラスでラップして機能追加したのがMySQLdbになります。
カーソルはMySQLdbで実装されているので接続に使うのはconnect()一択となります。
この辺ハマりポイントなので注意です！
(connect()使ってるくせに変数名はconnectionとかにする罠サンプルが多い！)

## とにかくSELECTだ、SELECTにかけろ！
接続できたらさっそくSELECT文でもやってみましょう。
以降接続済みのconnオブジェクトがある前提で書きますよ。

```
cursor = conn.cursor()
try:
  cursor.execute('select * from hoge_table where id=1')
  result = cursor.fetchall()
finally:
  cursor.close()
  conn.close()
```

カーソルとは何でしょうか？一言では言えない概念。本当に概念。
DBに対する複数操作を順次行うための仮想領域と説明する人もいるので
そんな感じだと思ってください。ちゃんと知りたい人はネットの海へGO！
使わなくてもconn.query('select ~')とかでSQL発行はできますが、
複数操作のトランザクション実行ができないのでDB操作はカーソルを使うのが一般的です。

さらにSQL文は発行しただけではオブジェクトとして取り出せず、結果セットとして
取得する必要があります。それがcursor.fetchall()になります。
他にもfetchone()とかfetchmany()とかありますが必要に応じて使い分ければいいんじゃないかな。

取り出したデータは例えばテーブルが
hoge_table

|id|name|hoge|
|:-----------|:-----------|:-----------|
|1|hogee|ホゲです|
|2|hogege|ホゲです?|

で、

```
cursor.execute('select * from hoge_table')
result = cursor.fetchall()
```
と取ると(ざっくり省略)、resultの中身は

```
((1, 'hogee', 'ホゲです'), (2, 'hogege', 'ホゲです?'))
```
となります。
え？カラム名は？と思われたでしょうが、
**それは甘え**
O/Rマッパー様に感謝しつつ自分でカラム名リストでも用意しましょう。

最後にfinally句でカーソルと接続の解放をしていますが、
**ぶっちゃけwith句使えばいらん**
なのですが、お作法としては知っておいて損はないです。
他の言語では自分で書かなきゃならないこともなきにしもあらずなので。
~~まあ解放しなくてもそうそうぶっ壊れないけどな！~~
貴重なリソースを速やかに解放することはプログラマの義務です

## よろしい、ならばUPDATEだ！
次は更新もしてみましょう。Pythonスクリプトのバッチ処理で一括更新とかできたら便利やん？

```
conn = MySQLdb.connect(host='hoge_host', user='hoge_user', passwd='hogehoge', db='hoge1')
conn.autocommit(False)

cursor = conn.cursor()
try:
  cursor.execute('update hoge_table set hoge="ホゲだよ！" where id=1')
  cursor.execute('select * from hoge_table')
  result = cursor.fetchall()
  conn.commit()
except Exception as e:
  conn.rollback()
  raise e
finally:
  cursor.close()
  conn.close()
```

2行目でautocommitをFalseにしていますが確かデフォルトFalseなので不要と言えば不要です。
まあコネクタにもよるかもしれないので明示的にFalseにした方が安心でしょう。
Trueにした場合は勝手にコミットします(そのまんま)。Falseなら自分でcommit()を呼びます。
rollback()も自前で呼びます。裏で勝手にやられるくらいならこの程度の手間はかけた方がご安心です。

UPDATE文の次の行でSELECT文は発行してますが、これは更新結果のバリデーションとかも
これでできるよ！という例です。結果が気に入らなかったら例外出してロールバックしてもらいましょう。
カーソル使わないとこの辺ができないのでやはり接続はconnect()一択です。
DBへの本反映はcommit()関数呼びで行われます。

-----------------
今回は以上になります。これで不意にO/RマッパーなしのDB操作の
タスクが入ってきても対応できるでしょう。
また、普段フレームワークが代行してくれていることもちゃんと中身を
知ればさらなる理解の助けになるかと思います。

> 「ちょっと待って！INSERTとかDELETEが入ってないやん」

やることはUPDATEと大して変わらないので省略です

> 「ちょっと待って！ALTERが入ってないやん」

それスクリプトでやる必要ある？