使用pandas dataframe的to_sql方法,我可以很容易地在oracle数据库中的表中写入少量行:
from sqlalchemy import create_engine
import cx_Oracle
dsn_tns = "(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=<host>)(PORT=1521))\
(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=<servicename>)))"
pwd = input('Please type in password:')
engine = create_engine('oracle+cx_oracle://myusername:' + pwd + '@%s' % dsn_tns)
df.to_sql('test_table', engine.connect(), if_exists='replace')
但是对于任何常规大小的数据帧(我的有60k行,不是那么大),代码变得无法使用,因为它在我愿意等待(绝对超过10分钟)的时间内从未完成。我已经搜索了很多次搜索和搜索,最接近的解决方案是ansonw在this question中给出的答案。但那个是关于mysql,而不是oracle。正如Ziggy Eunicien指出的那样,它对甲骨文不起作用。有什么想法吗?
修改
这是数据框中的行样本:
id name premium created_date init_p term_number uprate value score group action_reason
160442353 LDP: Review 1295.619617 2014-01-20 1130.75 1 7 -42 236.328243 6 pass
164623435 TRU: Referral 453.224880 2014-05-20 0.00 11 NaN -55 38.783290 1 suppress
这里是df的数据类型:
id int64
name object
premium float64
created_date object
init_p float64
term_number float64
uprate float64
value float64
score float64
group int64
action_reason object
答案 0 :(得分:14)
Pandas + SQLAlchemy默认情况下将所有object
(字符串)列保存为Oracle DB中的 CLOB ,这使得插入非常缓慢。
以下是一些测试:
import pandas as pd
import cx_Oracle
from sqlalchemy import types, create_engine
#######################################################
### DB connection strings config
#######################################################
tns = """
(DESCRIPTION =
(ADDRESS = (PROTOCOL = TCP)(HOST = my-db-scan)(PORT = 1521))
(CONNECT_DATA =
(SERVER = DEDICATED)
(SERVICE_NAME = my_service_name)
)
)
"""
usr = "test"
pwd = "my_oracle_password"
engine = create_engine('oracle+cx_oracle://%s:%s@%s' % (usr, pwd, tns))
# sample DF [shape: `(2000, 11)`]
# i took your 2 rows DF and replicated it: `df = pd.concat([df]* 10**3, ignore_index=True)`
df = pd.read_csv('/path/to/file.csv')
DF信息:
In [61]: df.shape
Out[61]: (2000, 11)
In [62]: df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 2000 entries, 0 to 1999
Data columns (total 11 columns):
id 2000 non-null int64
name 2000 non-null object
premium 2000 non-null float64
created_date 2000 non-null datetime64[ns]
init_p 2000 non-null float64
term_number 2000 non-null int64
uprate 1000 non-null float64
value 2000 non-null int64
score 2000 non-null float64
group 2000 non-null int64
action_reason 2000 non-null object
dtypes: datetime64[ns](1), float64(4), int64(4), object(2)
memory usage: 172.0+ KB
让我们检查将它存储到Oracle DB需要多长时间:
In [57]: df.shape
Out[57]: (2000, 11)
In [58]: %timeit -n 1 -r 1 df.to_sql('test_table', engine, index=False, if_exists='replace')
1 loop, best of 1: 16 s per loop
在Oracle DB中(注意CLOB):
AAA> desc test.test_table
Name Null? Type
------------------------------- -------- ------------------
ID NUMBER(19)
NAME CLOB # !!!
PREMIUM FLOAT(126)
CREATED_DATE DATE
INIT_P FLOAT(126)
TERM_NUMBER NUMBER(19)
UPRATE FLOAT(126)
VALUE NUMBER(19)
SCORE FLOAT(126)
group NUMBER(19)
ACTION_REASON CLOB # !!!
现在让我们指示pandas将所有object
列保存为VARCHAR数据类型:
In [59]: dtyp = {c:types.VARCHAR(df[c].str.len().max())
...: for c in df.columns[df.dtypes == 'object'].tolist()}
...:
In [60]: %timeit -n 1 -r 1 df.to_sql('test_table', engine, index=False, if_exists='replace', dtype=dtyp)
1 loop, best of 1: 335 ms per loop
这次是约。快了48倍
签入Oracle DB:
AAA> desc test.test_table
Name Null? Type
----------------------------- -------- ---------------------
ID NUMBER(19)
NAME VARCHAR2(13 CHAR) # !!!
PREMIUM FLOAT(126)
CREATED_DATE DATE
INIT_P FLOAT(126)
TERM_NUMBER NUMBER(19)
UPRATE FLOAT(126)
VALUE NUMBER(19)
SCORE FLOAT(126)
group NUMBER(19)
ACTION_REASON VARCHAR2(8 CHAR) # !!!
让我们用200.000行DF测试它:
In [69]: df.shape
Out[69]: (200000, 11)
In [70]: %timeit -n 1 -r 1 df.to_sql('test_table', engine, index=False, if_exists='replace', dtype=dtyp, chunksize=10**4)
1 loop, best of 1: 4.68 s per loop
在我的测试(不是最快的)环境中,200K行DF需要大约5秒钟。
结论:在将DataFrames保存到Oracle DB时,使用以下技巧为dtype
dtype的所有DF列显式指定object
。否则它将被保存为CLOB数据类型,这需要特殊处理并使其非常慢
dtyp = {c:types.VARCHAR(df[c].str.len().max())
for c in df.columns[df.dtypes == 'object'].tolist()}
df.to_sql(..., dtype=dtyp)
答案 1 :(得分:1)
您可以只使用 method='multi',这将提高您的数据插入速度。
您也可以根据需要调整块大小,具体取决于您的数据。
当我尝试编写一个能够将数据从 csv 文件/excel 加载到数据框中的谷歌云函数时发现了这一点,我想将该数据框保存到谷歌云 sql 中的 postgresql 数据库中。
如果您可以在数据框中创建与数据库表中类似的结构,那么这是一个非常方便的工具。
df.to_sql('table_name', con=engine, if_exists='append', index=False, chunksize=2000, method='multi')
答案 2 :(得分:0)
请在此处评论后代。我在python 3.6.8,pandas 1.1.3,sqlalchemy 1.3.20上。当我尝试从MaxU实施解决方案时,最初遇到错误:
raise ValueError(f"{col} ({my_type}) not a string")
老实说,我不知道为什么。经过几个小时的调试,这终于对我有用。就我而言,我试图从CSV读取并插入Oracle:
import cx_Oracle
import numpy as np
import pandas as pd
import sqlalchemy as sa
from sqlalchemy import create_engine
conn = create_engine('oracle://{}:{}@{}'.format(USERNAME, PASSWORD, DATABASE))
df = pd.read_csv(...)
object_columns = [c for c in df.columns[df.dtypes == 'object'].tolist()]
dtyp = {c:sa.types.VARCHAR(df[c].str.len().max()) for c in object_columns}
df.to_sql(..., dtype=dtyp)
说实话,我并没有做太大改变,所以不能100%知道为什么我得到了原始错误,只是在这里发帖以防它有所帮助。
答案 3 :(得分:0)
使用此功能:
此函数将采用数据帧名称和长度(可选)。
它将转换后的数据类型(对象类型)返回到 Varchar(length) 默认 lenght =250 (这个乐趣只处理对象类型)
dtest[, sequence := rep(seq_len(floor(.N/5)),length.out=.N), by = places]
dtest[sequence!=1,sequence:=NA]
调用方法示例:
def dtyp(df_name, length=250):
cols = df_name.select_dtypes(include='object')
dtyps = {col: VARCHAR2(length) for col in cols}
return dtypsenter code here