web-dev-qa-db-ja.com

Python Pandas to_sql、主キーを持つテーブルを作成するには?

次のように、主キーを持つPandasのto_sql関数を使用してMySQLテーブルを作成したいと思います(通常、mysqlテーブルに主キーがあると便利です)。

group_export.to_sql(con = db, name = config.table_group_export, if_exists = 'replace', flavor = 'mysql', index = False)

しかし、これにより、主キーなしで(またはインデックスなしでも)テーブルが作成されます。

ドキュメントには、「index」パラメータと組み合わせてインデックスを作成できるパラメータ「index_label」が記載されていますが、主キーのオプションは記載されていません。

ドキュメント

22
patapouf_ai

免責事項:この答えは実用的というより実験的ですが、言及する価値があるかもしれません。

クラスpandas.io.sql.SQLTableには名前付き引数keyがあり、フィールドの名前を割り当てると、このフィールドが主キーになることがわかりました。

残念ながら、この引数をDataFrame.to_sql()関数から転送することはできません。それを使用するには、次のことを行う必要があります。

  1. pandas.io.SQLDatabaseインスタンスを作成します

    engine = sa.create_engine('postgresql:///somedb')
    pandas_sql = pd.io.sql.pandasSQL_builder(engine, schema=None, flavor=None)
    
  2. pandas.io.SQLDatabase.to_sql()に似た関数を定義しますが、その中に作成された*kwargsオブジェクトに渡されるpandas.io.SQLTable引数を追加します(元のto_sql()メソッドをコピーし、*kwargs):

    def to_sql_k(self, frame, name, if_exists='fail', index=True,
               index_label=None, schema=None, chunksize=None, dtype=None, **kwargs):
        if dtype is not None:
            from sqlalchemy.types import to_instance, TypeEngine
            for col, my_type in dtype.items():
                if not isinstance(to_instance(my_type), TypeEngine):
                    raise ValueError('The type of %s is not a SQLAlchemy '
                                     'type ' % col)
    
        table = pd.io.sql.SQLTable(name, self, frame=frame, index=index,
                         if_exists=if_exists, index_label=index_label,
                         schema=schema, dtype=dtype, **kwargs)
        table.create()
        table.insert(chunksize)
    
  3. SQLDatabaseインスタンスと保存したいデータフレームでこの関数を呼び出します

    to_sql_k(pandas_sql, df2save, 'tmp',
            index=True, index_label='id', keys='id', if_exists='replace')
    

そして、次のようなものが得られます

CREATE TABLE public.tmp
(
  id bigint NOT NULL DEFAULT nextval('tmp_id_seq'::regclass),
...
)

データベース内。

PSもちろん、モンキーパッチDataFrameio.SQLDatabase、およびio.to_sql()関数を使用して、この回避策を便利に使用できます。

14
krvkir

パンダでテーブルをアップロードした後、主キーを追加するだけです。

group_export.to_sql(con=engine, name=example_table, if_exists='replace', 
                    flavor='mysql', index=False)

with engine.connect() as con:
    con.execute('ALTER TABLE `example_table` ADD PRIMARY KEY (`ID_column`);')
32
tomp

automap_base from sqlalchemy.ext.automap(tableNamesDictは、Pandasテーブル)のみを持つ辞書です。

metadata = MetaData()
metadata.reflect(db.engine, only=tableNamesDict.values())
Base = automap_base(metadata=metadata)
Base.prepare()

これは完全に機能しますが、1つの問題を除いて、自動マップにはテーブルに主キーが必要です。 OK、問題ない、きっとPandas to_sqlには主キーを示す方法があります...これは少しハックがかかるところです:

for df in dfs.keys():
    cols = dfs[df].columns
    cols = [str(col) for col in cols if 'id' in col.lower()]
    schema = pd.io.sql.get_schema(dfs[df],df, con=db.engine, keys=cols)
    db.engine.execute('DROP TABLE ' + df + ';')
    db.engine.execute(schema)
    dfs[df].to_sql(df,con=db.engine, index=False, if_exists='append')

dictDataFramesを反復処理し、主キー(つまり、idを含む列)に使用する列のリストを取得し、get_schemaを使用して空のテーブルを作成し、DataFrameをテーブルに追加します。

モデルができたので、明示的に名前を付けて使用できます(つまり、User = Base.classes.usersession.queryまたは、次のようなすべてのクラスの辞書を作成します。

alchemyClassDict = {}
for t in Base.classes.keys():
    alchemyClassDict[t] = Base.classes[t]

そしてクエリ:

res = db.session.query(alchemyClassDict['user']).first()