【问题标题】:How to insert empty excel dates into oracle with Python+Pandas?如何使用 Python+Pandas 将空的 excel 日期插入到 oracle 中?
【发布时间】:2014-12-22 20:20:45
【问题描述】:

我有一个 Python 应用程序,它使用 pandas 来处理一些 excel 电子表格并将值插入到 oracle 数据库中。

对于具有值的日期单元格,这可以正常工作。对于空日期单元格,我插入了一个 NaT,我认为这会很好,但在 Oracle 中,这正在变成一些奇怪的无效时间,显示为“0001-255-255 00:00:00”(类似于 MAXINT 或 0正在转换为我猜的时间戳?)

In[72]: x.iloc[0][9]
Out[72]: NaT

上面是DataFrame中的一小段数据,你可以看到它是一个NaT。

但这就是我在 Oracle 中看到的......

SQL> select TDATE from TABLE where id=5067 AND version=5;

TDATE
---------
01-NOVEMB

SQL> select dump("TDATE") TABLE where id=5067 AND version=5;

DUMP("TDATE")
--------------------------------------------------------------------------------
Typ=12 Len=7: 100,101,255,255,1,1,1

我尝试使用 df.replace 和/或 df.where 将 NaT 转换为 None,但其中任何一个都出现了各种错误,这似乎暗示替换以这种方式无效。

有什么方法可以确保这些数据存储中空日期的一致性?!

【问题讨论】:

  • 如何在数据库中插入日期值?日期列的类型是什么?
  • 欢迎来到 Stack Overflow。请查看我的回答,如果您有任何未解决的问题,请在我的回答中发表评论:-)
  • @SylvainLeroux In[231]: x['TDATE'].dtype Out[229]: dtype('
  • 我尝试了将近 1/2 小时,但我无法重现该行为(即:“伪造”日期以产生与您相同的“转储”)。您是否使用DataFrame.to_sql 将数据帧发送到数据库?还是别的什么?
  • 不,由于太深奥而无法进入,我正在稍微按摩 DataFrame 并实际使用游标直接执行 sql。鉴于您没有看到 .to_sql 的怪异之处,可能值得我测试该方法。如果可行,我可以解决导致我改用光标的问题。

标签: python excel oracle pandas


【解决方案1】:

此问题已在 Pandas 15.0 中修复。

如果可以,请更新到 Pandas >= 15.0。从该版本开始,NaNNaT 在数据库中正确存储为 NULL。


在进行了一些实验之后,Pandas 似乎将 NaT 传递给 SQLAlchemy 并传递给 cx_Oracle——后者又盲目地向 Oracle 发送一个无效日期(反过来它不会抱怨)。

无论如何,我能够添加一个BEFORE INSERT TRIGGER 来修复传入的时间戳。为此,您必须先手动创建表。

-- Create the table
CREATE TABLE W ("ID" NUMBER(5), "TDATE" TIMESTAMP);

然后是触发器:

-- Create a trigger on the table
CREATE OR REPLACE TRIGGER fix_null_ts
BEFORE INSERT ON W
FOR EACH ROW WHEN (extract(month from new.tdate) = 255)
BEGIN
  :new.tdate := NULL;
END;
/

之后,在 Python 中,使用 pandas.DataFrame.toSql(..., if_exists='append'):

>>> d = [{"id":1,"tdate":datetime.now()},{"id":2}]
>>> f = pd.DataFrame(d)
>>> f.to_sql("W",engine, if_exists='append', index=False)
#                        ^^^^^^^^^^^^^^^^^^
#          don't drop the table! append data to an existing table

然后检查:

>>> result = engine.execute("select * from w")
>>> for row in result:
...     print(row)
... 
(1, datetime.datetime(2014, 10, 31, 1, 10, 2))
(2, None)

请注意,如果您需要将另一个 DataFrame 重写到同一个表,您首先需要删除它的内容 - 但不要删除它,否则您会同时失去触发器。例如:

# Some new data
>>> d = [{"id":3}]
>>> f = pd.DataFrame(d)

# Truncate the table and write the new data
>>> engine.execute("truncate table w")
>>> f.to_sql("W",engine, if_exists='append', index=False)
>>> result = engine.execute("select * from w")

# Check the result
>>> for row in result:
...     print(row)
... 
(3, None)

【讨论】:

  • 谢谢!虽然我还不能升级 pandas(目前离发布太近,无法升级主要组件),但使用你给出的一堆指针,我能够在短期内克服这个问题。大熊猫的长期复兴已经在计划之中,这样会更好。谢谢!
【解决方案2】:

我希望Oracle数据库中日期列的数据类型是DATE

在这种情况下,请记住,日期有日期部分和时间部分一起作为日期。加载到数据库时,请确保使用 TO_DATE 并将正确的日期时间格式放入日期文字。

这是关于加载的。现在,要显示,请使用具有正确日期时间格式的TO_CHAR 以人眼希望看到日期时间值的方式查看值。

而且,关于 NULL 值,除非您有 NOT NULL 约束,否则我看不到加载有任何问题。 NULL 值无论如何都会加载为 NULL。如果要操作 NULL 值,请使用 NVL 函数并使用要替换 NULL 值的所需值。

【讨论】:

  • "我希望Oracle数据库中日期列的数据类型是DATE。"根据DUMP的输出是data type 12 is DATE
猜你喜欢
  • 2018-09-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-07-13
  • 2021-01-20
  • 2020-01-09
  • 1970-01-01
  • 2018-11-13
相关资源
最近更新 更多