LoginSignup
0
1

More than 3 years have passed since last update.

Pandas で SQLite の Upsert(車輪の再開発)

Posted at

PandasでUpsertが待ちきれず、自作してみた

PandasのSql Upsertを試してみた を見て、Upsert なる言い方を知った(SQLiteの"INSERT OR REPLACE"は大昔使っていたけど)

これこれ、と思ったけど、まだリリースされていないいし(21年4月現在)、なんとPandas の to_sqlだと、primary keyやunique keyが作れない。
SQLTable/SQLiteTableにはkeysで指定できるけど、to_sqlにない。Why?)

とりあえず、下記を参考にして、SQLite専用だけどUpsertを作ってみた。
Python Pandas to_sql, how to create a table with a primary key?

def upsert(frame, name: str, unique_index_label, con):
    pandas_sql = pd.io.sql.pandasSQL_builder(con)

    if isinstance(frame, pd.Series):
        frame = frame.to_frame()
    elif not isinstance(frame, pd.DataFrame):
        raise NotImplementedError(
            "'frame' argument should be either a Series or a DataFrame"
        )

    # table = pd.io.sql.SQLiteTable(name, pandas_sql, frame=frame, index=False, if_exists='append')
    table = pd.io.sql.SQLiteTable(name, pandas_sql, frame=frame, index=False, if_exists='append', keys=unique_index_label)
    table.create()
    # pandas_sql.execute('CREATE UNIQUE INDEX IF NOT EXISTS "{0}_{1}" ON "{0}" ("{1}");'.format(table.name, unique_index_label))

    def _execute_insert(self, conn, keys, data_iter):
        wld = "?"  # wildcard char
        escape = pd.io.sql._get_valid_sqlite_name

        bracketed_names = [escape(str(column)) for column in keys]
        col_names = ",".join(bracketed_names)
        wildcards = ",".join([wld] * len(bracketed_names))
        insert_statement = (
            f"INSERT OR REPLACE INTO {escape(self.name)} ({col_names}) VALUES ({wildcards})"
        )
        data_list = list(data_iter)
        conn.executemany(insert_statement, data_list)

    table.insert(method=_execute_insert)

こんな感じで使用

import pandas as pd
import sqlite3
from contextlib import closing

large_category_list = []
middle_category_list = []
for large_category in large_categories:
    large_category_list.append(dict(id=large_category['id'], name=large_category['name']))
    for middle_category in large_category['middle_categories']:
        middle_category_list.append(dict(large_category_id=large_category['id'], id=middle_category['id'], name=middle_category['name']))

large_category_df = pd.DataFrame(large_category_list)
middle_category_df = pd.DataFrame(middle_category_list)

with closing(sqlite3.connect(args.sqlite)) as con:
    upsert(large_category_df, 'large_categories', 'id', con)
    upsert(middle_category_df, 'middle_categories', 'id', con)

ちなみに、sqlite3には、下記を使ってExcelからもアクセス可能。
ExcelのTableの機能と合わせると結構便利かも(試行錯誤中)。
Webから取得したデータはSQLiteで管理(更新されたらupsertで対応)。
分析やデータの加工はExcelのテーブルとかで対応。

SQLite ODBC Driver
32/64bitに注意。Excelは32bitだと思い込んでいてえらい時間くった。
インストールしたけど使えないのが、(ODBC初めてで)使い方ミスったと思って、インストールミスにすぐ気が付かなかった。。。

0
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
0
1