【发布时间】:2021-07-08 11:39:26
【问题描述】:
在我的数据库 ddl (SQL Server) 中,我的字段具有默认值:
my_database_field varchar(255) NOT NULL
DEFAULT 'NVT'
但是,当我尝试使用 DataFrame.to_sql() 将数据插入此表时,我收到以下错误:
Cannot insert the value NULL into column 'my_database_field', table 'MYDB.dbo.my_table'; column does not allow nulls. INSERT fails.
代码如下:
with engine.begin() as conn:
dataframe.to_sql(table_name, conn, index=False, if_exists='append')
“my_database_field”列具有 NaN 值:
data['my_database_field']
0 NaN
1 NaN
2 NaN
3 NaN
4 NaN
..
4154 NaN
4155 NaN
4156 NaN
4157 NaN
4158 NaN
这似乎是因为 to_sql 方法正在用 NULL 替换 NaN 值(可能应该是这样)
我想要插入 NaN 值的选项,并让数据库使用架构中定义的 DEFAULT 值,而不是在使用 to_sql 方法时直接插入 NULL。
我尝试用空字符串替换数据框中的所有 NaN,但是这也遇到了同样的问题。它将那些空字符串传播到数据库,这就是显示的内容,而不是 DDL 中指定的默认值。
我可以让它工作的唯一方法是根本不使用 pandas dataframe.to_sql 方法,而是从 csv 文件加载数据并使用基本的 sqlalchemy SQL 表达式逐行写入数据库。
我在 pandas github 中打开了一个问题:https://github.com/pandas-dev/pandas/issues/42408
编辑:根据@Larnu 的评论,我缺少的关键信息是:
仅当您未在 INSERT 中指定列时才使用 DEFAULT 值。如果您(或应用程序)显式提供 NULL 值,则将插入 NULL 值。 DEFAULT 不是“在提供 NULL 时使用此值”属性,而是“在省略列时使用此值”
现在的问题变成了如何优雅地处理插入一些列包含一些 NaN 的大型数据框。我想避免不得不逐行插入数据,并检查该行中的任何值是否为 NaN。
编辑 2:我通过使用 ORM 解决了这个问题,如下所示:
Base = declarative_base()
class MyModel(Base):
__tablename__ = 'my_table'
my_id = Column(String, primary_key=True)
my_integer_field = Column(Integer)
my_database_field = Column(String, server_default='NVT')
@staticmethod
def from_dict(input_dict: dict):
for k, v in input_dict.items():
logging.debug(f"Key: {k} - Value: {v}")
if type(v) is str:
continue
try:
if np.isnan(v):
input_dict[k] = None
except TypeError as e:
logging.debug(e)
logging.debug(f"Could not test if: {type(v)}:{v} was NaN")
return __class__(**input_dict)
如您所见,我添加了一个名为“from_dict”的自定义静态方法,它根据字典构建我的模型。对于每个值,我检查它是否是 np.nan,如果是,我将其设置为 None。这似乎使 ORM 在幕后的 insert 方法中不包含该字段,因此让数据库使用默认值。 np.isnan() 方法只适用于数字而不是字符串,所以对于那些我只是捕捉到异常的人,注销一些信息并继续前进。
对于字符串值,我似乎必须在模型本身中设置“server_default”。如果有人知道更多关于这里发生的事情,请随时分享。
【问题讨论】:
-
DEFAULT值仅在您未在INSERT中指定列时使用。如果您(或应用程序)明确提供NULL值,则将插入NULL值。DEFAULT不是“在提供NULL时使用此值”属性,而是“在省略列时使用此值”属性。 -
@Larnu 说得好。我不知道的东西。关于如何处理这个问题的任何建议?我需要批量插入这些数据,其中一些有值,其余的是 NaN。我想避免逐行检查给定行是否具有 NaN 值,如果有,则删除列标题。
-
对不起,如果我错过了这个,但是“my_database_field”是您唯一关心的使用一些默认值的列吗?
-
让 pandas 输入“默认”值有什么问题吗?
.fillna('NVT') -
看来您需要先在Python端处理数据框,即:用数据库中指定的列默认值替换
NaN值。
标签: python sql sql-server pandas