通过pandas.to_sql插入枚举数据类型

k10s72fa  于 2023-08-01  发布在  其他
关注(0)|答案(2)|浏览(85)

我有一个pandas dataframe如下:

In [93]: df = pd.DataFrame([[1, 'a'], [2, 'b'], [3, 'a']], columns=['val', 'types'])

In [94]: df
Out[94]:
   val types
0    1    a
1    2    b
2    3    a

In [95]: df.dtypes
Out[95]:
val       int64
types    object
dtype: object

# convert the 'types' column to category
In [96]: df.types = pd.Categorical(df.types)

字符串
假设我已经有一个postgres表,列typesENUM,我如何使用pandas to_sql方法通过sqlachemy类型将数据插入到我的DB中?
我试过这个,但无法让它工作:

In [101]: class myEnum(enum.Enum):
     ...:     a = 1
     ...:     b = 2
     ...:

In [102]: from sqlalchemy.types import *

In [103]: sql_dtypes = {'val' : SmallInteger(), 'types': Enum(myEnum)}

In [104]: df.to_sql('tablename', conn, if_exists='replace', index=False,
                    dtype=sql_dtypes)


运行时会出现以下错误:

sqlalchemy.exc.ProgrammingError: (psycopg2.ProgrammingError) type "myEnum" already 
exists

[SQL: "CREATE TYPE myEnum AS ENUM ('a', 'b')"] (Background on this 
error at: http://sqlalche.me/e/f405)


使用pd.io.json.build_table_schema中的schema选项进行指定也没有帮助。

puruo6ea

puruo6ea1#

要在pandas.to_sql中处理postgresql enum,我们应该执行以下操作:
试图用一个相关的例子来解释,以便更容易阅读。
在pandas之外的数据库DDL中。所以Postgresql Enum是在pandas作用域之外创建的。

-- In postgresql
CREATE TYPE bank_transaction_type AS ENUM
     ('withdrawal', 'deposit');

CREATE TABLE bank_transactions (
    bank_name text,
    reference_no text, 
    transaction_date date, 
    transaction_type bank_transaction_type, 
);

字符串
用Pandas文字

import pandas as pd
from pandas import DataFrame
from sqlalchemy.dialects.postgresql import ENUM

BankTransactionType = ENUM("withdrawal", "deposit", 
                           name="bank_transaction_type",
                           create_type=False)

def save_df_to_postgres(df: DataFrame, table_name: str):
    try:
        sql_dtypes = {"transaction_type": BankTransactionType}
        df.to_sql(
            name=table_name, 
            con=settings.sqlalchemy_database_url,
            if_exists="append",
            index=False,
            dtype=sql_dtypes,
            method='multi'
        )
    except Exception as e:
        logger.error(f"Error while saving to postgres: {e}")


几点建议:

  • ENUM来自sqlalchemy.dialects.postgresql,而不是python的Enum
  • sql_dtypes仅定义列的子集,而不是所有列类型。这应该有助于保存手动工作
  • to_sql中-dtype=sql_dtypes被分配了字典Map到sqlalchemy枚举类型
  • BankTransactionType创建中,我们提到-create_type=False以跳过这种类型的创建。这在pandas.to_sql之外进行处理

希望这对某人有帮助。

mwecs4sa

mwecs4sa2#

我现在从here找到的一个解决方法是首先将数据类型写为varchar,然后将其转换为enum

In [104]: df.to_sql('tablename', conn, if_exists='replace', index=False,
                    dtype=sql_dtypes)

In [105]: conn.execute("""ALTER TABLE tablename 
                          ALTER COLUMN types TYPE myEnum using types::myEnum;""")

字符串
但我希望有更好的解决办法。

相关问题