LoginSignup
1
1

More than 5 years have passed since last update.

[初級]pythonでMariaDBへの登録(INSERT)で重複データを登録しない方法

Posted at

目的

pythonでmariaDBの操作を勉強している際に、
同じデータが重複して登録(INSERT)できてしまったため、それを回避するためにどうすればいいのかメモメモ。

前提

・プログラム
python3.7
・モジュール
pymysql

・データベース
mariaDB

失敗例

INSERTのPythonコードは下記で実施した。

mysql_insert.py
connection = pymysql.connect(host='127.0.0.1',
                             port = 3306,
                             user='root',
                             password='password',
                             db='db_name',
                             charset='utf8',
                             # cursorclassを指定することで
                             # Select結果をtupleではなくdictionaryで受け取れる
                             cursorclass=pymysql.cursors.DictCursor)
# Insert処理
try:
    with connection.cursor() as cursor:
        sql = "INSERT INTO テーブル名 (id, name) VALUES (%s, %s)"
        r = cursor.execute(sql, (id, name))
        print(r) # -> 1
        cursor.close()
    # autocommitではないので、明示的にコミットする
    connection.commit()
# MySQLから切断する
finally:
    connection.close()

実行結果は問題ないが、同じデータで複数回書き込みを行うと、
書き込みした分だけ同じデータが追加される。

テーブル名.db
+-----+-------+
| id  | name  |
+-----+-------+
| 1   | name1 |
| 1   | name1 | # 同じデータが登録される
+-----+-------+

これではデータの更新ができず、永遠に同じデータが登録されてしまう。

[修正1]データベースの修正

そもそもデータベースの基礎がわかってればここは回避できる。
必要なことは「ユニークキー制約」が設定されていること。

ユニークキー制約とは?
データベースの項目に付与する「他の行の値と重複しちゃダメよ制約」のこと。

まずはテーブルのカラム情報を確認していきます。

 show columns from テーブル名;
+-------+-------------+------+-----+---------+-------+
| Field | Type        | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+-------+
| id    | bigint(20)  | YES  |     | NULL    |       |
| name  | varchar(50) | YES  |     | NULL    |       |
+-------+-------------+------+-----+---------+-------+

上記カラム情報の「Key」を確認すると何も記載されていないのがわかる。

今回はすでにカラムまで作成していたのでユニークキーを追加。
下記SQLコマンドを実行します。
alter table テーブル名 add unique (カラム名);

実行後再度確認します。

show columns from テーブル名;
+-------+-------------+------+-----+---------+-------+
| Field | Type        | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+-------+
| id    | bigint(20)  | YES  | UNI | NULL    |       |
| name  | varchar(50) | YES  |     | NULL    |       |
+-------+-------------+------+-----+---------+-------+

idのカラムのKey情報に「UNI」と追加されているのがわかります。
これでデータベースはOK。

[修正2]Pythonの修正

データベースを修正後、Pythonを実行したらエラーとなった。

Traceback (most recent call last):
  File "get_profiles.py", line 60, in <module>
  File "E:\anaconda\lib\site-packages\pymysql\cursors.py", line 170, in execute
    result = self._query(query)
  File "E:\anaconda\lib\site-packages\pymysql\cursors.py", line 328, in _query
    conn.query(q)
  File "E:\anaconda\lib\site-packages\pymysql\connections.py", line 517, in query
    self._affected_rows = self._read_query_result(unbuffered=unbuffered)
  File "E:\anaconda\lib\site-packages\pymysql\connections.py", line 732, in _read_query_result
    result.read()
  File "E:\anaconda\lib\site-packages\pymysql\connections.py", line 1075, in read
    first_packet = self.connection._read_packet()
  File "E:\anaconda\lib\site-packages\pymysql\connections.py", line 684, in _read_packet
    packet.check_error()
  File "E:\anaconda\lib\site-packages\pymysql\protocol.py", line 220, in check_error
    err.raise_mysql_exception(self._data)
  File "E:\anaconda\lib\site-packages\pymysql\err.py", line 109, in raise_mysql_exception
    raise errorclass(errno, errval)
pymysql.err.IntegrityError: (1062, "Duplicate entry '1' for key 'id'")

これはSQL分に「IGNORE」を追加することで回避可能。

sql = "INSERT IGNORE INTO テーブル名 (id, name) VALUES (%s, %s)"

そもそも「IGNORE」は
INSERT ステートメントの実行中に発生したエラーは無視するだけのもの。
使い方には要注意かもね!
ちなみに「IGNORE」追加後は警告がでるが実行はされる。

E:\anaconda\lib\site-packages\pymysql\cursors.py:329: Warning: (1062, "Duplicate entry '1' for key 'id'")
  self._do_get_result()
0

修正後のコード

connection = pymysql.connect(host='127.0.0.1',
                             port = 3306,
                             user='root',
                             password='password',
                             db='db_name',
                             charset='utf8',
                             # cursorclassを指定することで
                             # Select結果をtupleではなくdictionaryで受け取れる
                             cursorclass=pymysql.cursors.DictCursor)
# Insert処理
try:
    with connection.cursor() as cursor:
        sql = "INSERT IGNORE INTO  テーブル名 (id, name) VALUES (%s, %s)"
        r = cursor.execute(sql, (id, name))
        print(r) # -> 1
        cursor.close()
    # autocommitではないので、明示的にコミットする
    connection.commit()
# MySQLから切断する
finally:
    connection.close()

注意

これではデータの更新はできないので更新したい場合はUPDATE文が必要。
新規登録時は上記対応で。登録済みの場合はUPDATE分に移行する処理が必要かな~!
更新処理はこれから対応します!!

メモ終了。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1