PythonでmariaDBの重複データを削除する
さてさて、今日はOS関連のネタではなく、個人で運用しているデータベース関連のお話。
データベースを運用していると、稀に重複したデータが登録されていることがある、
業務での設計とは違い、個人で構築管理する場合、例外ケースを排除しきれない場合もある。
今回は、筆者が分析に利用するBitcoinのデータ取得DB上で重複データが見つかった。
そこで、pythonプログラムを用いて重複したデータの削除を行うことにした。
日付と時/分で重複データがあることがわかった。
(原因としてはサーバとのセッションが切れた際などの処理の可能性がある、そこまで考慮していない)
そこで、まずは重複したデータの検知の方法として、以下のSQL構文を用いた。
重複データを検出するSQL構文(group by + having句)
select date,hour,min from [テーブル名] where product_code='BTC_JPY' group by date,hour,min having count(*)>1;
このSQL構文をPythonに組み込むわけであるが、以下のようにした。
尚、pymysqlモジュールを別途インストールする必要があります。
$ python3 -m pip install pymysql
#!/usr/bin/python3
import pymysql.cursors
conn = pymysql.connect(user = 'user',
password = 'passpass',
host = '192.168.1.xxx',
db = 'db_name',
charset='utf8mb4')
def search_del_data(product_code):
con = conn.cursor()
sql= "select date,hour,min from bfonemin where product_code=%s group by date,hour,min having count(*)>1"
parm= (product_code)
con.execute(sql,parm)
result = con.fetchall()
print(result)
con.close()
search_del_data("BTC_JPY")
上記実施の結果は以下のように表示される。
以下はサンプルであり、実際には約2000レコードあった。
検出の結果データ
(('2021/10/30', '23', '41'), ('2021/10/31', '00', '31'), ('2021/10/31', '00', '34'), ('2021/10/31', '00', '39'), ('2021/10/31', '10', '24'), ('2021/10/31', '13', '16'), ('2021/10/31', '19', '43'), ('2021/10/31', '22', '51'), ('2021/10/31', '23', '25'), ('2021/11/01', '20', '14'), ('2021/11/01', '21', '14'), ('2021/11/01', '22', '07'), ('2021/11/01', '22', '33'), ('2021/11/03', '04', '59'), ('2021/11/03', '05', '56'), ('2021/11/03', '08', '01'), ('2021/11/03', '11', '13'), ('2021/11/03', '11', '37'), ('2021/11/03', '16', '36'), ('2021/11/03', '16', '43'), ('2021/11/03', '17', '32'), ('2021/11/03', '18', '46'), ('2021/11/03', '18', '48'), ('2021/11/03', '20', '02'), ('2021/11/03', '20', '30'), ('2021/11/03', '20', '40'), ('2021/11/03', '20', '42'), ('2021/11/03', '21', '20'), ('2021/11/03', '21', '49'), ('2021/11/03', '22', '15'), ('2021/11/03', '22', '27'), ('2021/11/03', '23', '06'))
2000レコードも手動で削除するわけにはいかない。
そこで、上記のコードを修正した。
検出結果をリストに格納し、DBから削除する
検出する関数(search_del_data)の他に、削除する関数(del_data)を作成。
len(result)で検出結果の数を取得し、それを元にwhile文で削除関数へ。
削除関数は日付データを元にkey となるIDを出力し、IDを元に削除。
これを繰り返すことで一括で削除を実施することができた。
削除作業を手動で行うことを考えたら、かなりの工数削減である。
#!/usr/bin/python3
import pymysql.cursors
conn = pymysql.connect(user = 'user',
password = 'passpass',
host = '192.168.1.xxx',
db = 'db_name',
charset='utf8mb4')
#
def search_del_data(product_code):
con = conn.cursor()
sql= "select date,hour,min from bfonemin where product_code=%s group by date,hour,min having count(*)>1"
parm= (product_code)
con.execute(sql,parm)
result = con.fetchall()
print(len(result))
i=0
while i < len(result):
del_date=result[i][0]
del_hour=result[i][1]
del_min=result[i][2]
i+=1
print(del_date,del_hour,del_min)
del_data(product_code,del_date,del_hour,del_min)
con.close()
#
def del_data(product_code,date,hour,min):
con = conn.cursor()
sql="select id from bfonemin where product_code=%s and date =%s and hour=%s and min = %s"
parm=(product_code,date,hour,min)
con.execute(sql,parm)
result = con.fetchall()
if len(result) > 1:
delete_id=result[0][0]
print(delete_id)
sql = "delete from bfonemin where id =%s"
con.execute(sql,delete_id)
conn.commit()
return result
con.close()
#
search_del_data('BTC_JPY')
search_del_data('ETH_JPY')
#
尚、筆者の場合は重複を1分毎に発見、削除できる仕組みを作るので、
このプログラムは役目は終えたわけですが、何かの参考になれば幸いです。