Pandas的to_sql函数使用索引的坑
1、语法
DataFrame.to_sql(name, con, schema=None, if_exists='fail', index=True, index_label=None, chunksize=None, dtype=None, method=None)
name:SQL表的名称。
con:sqlalchemy.engine.Engine
schema:指定数据库方案,对于MySQL,用None
if_exists:{‘fail’,‘replace’,‘append’},默认’fail’
- fail:引发ValueError。
- replace:在插入记录之前删除表,相当于drop table。
- append:将记录追加现有表,保留原有记录。
index:布尔值,默认为True,将DataFrame索引写为列。使用index_label作为表中的列名。
index_label:字符串或序列,默认为None,索引列的列标签。如果给出None(默认)且 index为True,则使用索引名称。如果DataFrame使用MultiIndex,则应该给出一个序列。
chunksize:int,可选,行将一次批量写入此大小。默认情况下,所有行都将立即写入。
dtype:dict,可选,指定列的数据类型。键应该是列名,字典形式储存:{column_name: sql_dtype}
to_sql 返回处理的记录数量。
2、测试数据
数据准备如下:
import numpy as np
import pandas as pd
from sqlalchemy import create_engine
import random
import string
engine = create_engine(
'mysql+mysqldb://root:root123@192.168.128.64:3306/dbmysql?charset=utf8&use_unicode=1')
lst_ind = []
lst_len = 6
for i in range(lst_len):
value = ''.join(random.sample(string.ascii_letters , 12))
lst_ind.append(value)
data = pd.DataFrame(np.random.rand(lst_len,4),index=lst_ind,columns=['col_1','col_2','col_3','col_4'])
data.index.name='id_name'
print(data)
数据结构如下:
col_1 col_2 col_3 col_4
id_name
BcjohMKsSxyw 0.193311 0.847717 0.283521 0.391023
PscnTGeDBYbX 0.599958 0.592000 0.513664 0.157038
bEjGKqeyosDP 0.761345 0.957985 0.624038 0.935655
NbohAxEkFMwu 0.915593 0.449361 0.635853 0.523836
pfnIbTmtzrWq 0.906764 0.055736 0.825208 0.336454
JcVtMhPSXgow 0.219105 0.674766 0.521235 0.101047
(1)append 无index
#1、将data写入数据库,不导入索引列,追加记录
respond = data.to_sql('tosql_table',engine,chunksize=1000,if_exists='append',index=False)
print('record: '+str(respond))
tosql_table表结构如下
Field Type NULL
col_1 double YES
col_2 double YES
col_3 double YES
col_4 double YES
可以多次执行本例中的to_sql函数,表中的记录不断增加。
(2)append有index
#2、将data写入数据库,导入索引列,追加记录
respond = data.to_sql('tosql_table',engine,chunksize=1000,if_exists='append',index=True)
print('record: '+str(respond))
**注意:**因为定义data的时候指定了索引,索引名是id_name ,在to_sql 中就不能指定index_label ,否则将报错。
tosql_table表结构如下:
Field Type NULL
id_name text YES
col_1 double YES
col_2 double YES
col_3 double YES
col_4 double YES
在数据库中查询表上的索引,结果是没有索引
show index in tosql_table;
(3)replace无index
#3、将data写入数据库,不导入索引列,删除表所有记录后,增加dataframe中的记录
respond = data.to_sql('tosql_table',engine,chunksize=1000,if_exists='replace',index=False)
print('record: '+str(respond))
成功导入数据。
tosql_table表结构如下:
Field Type NULL
col_1 double YES
col_2 double YES
col_3 double YES
col_4 double YES
(4)replace有index
最精彩的坑来了
#4、将data写入数据库,导入索引列,,删除表所有记录后,增加dataframe中的记录
respond = data.to_sql('tosql_table',engine,chunksize=1000,if_exists='replace',index=True)
print('record: '+str(respond))
执行失败,报错一大堆,在最后有用的提示,id_name 是TEXT,不能建索引:
OperationalError: (pymysql.err.OperationalError) (1170, "BLOB/TEXT column 'id_name' used in key specification without a key length")
[SQL: CREATE INDEX ix_tosql_table_id_name ON tosql_table (id_name)]
表的数据结构创建了,但是没有数据。
Field Type NULL
id_name text YES
col_1 double YES
col_2 double YES
col_3 double YES
col_4 double YES
强制转换字段类型:
respond = data.to_sql('tosql_table',engine,chunksize=1000,if_exists='replace',index=True,dtype={'id_name':VARCHAR(12)})
居然也报错,name ‘VARCHAR’ is not defined
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
Cell In[129], line 1
----> 1 respond = data.to_sql('tosql_table',engine,chunksize=1000,if_exists='replace',index=True,dtype={'id_name':VARCHAR(12)})
NameError: name 'VARCHAR' is not defined
没有办法了。
不用字符串了,转换到数据库就是TEXT 。索引字段直接用整型int ,看是否报错。
lst_len = 6
data = pd.DataFrame(np.random.rand(lst_len,4),index=range(lst_len),columns=['col_1','col_2','col_3','col_4'])
data.index.name='id_name'
col_1 col_2 col_3 col_4
id_name
0 0.639946 0.007668 0.550247 0.100629
1 0.784641 0.959056 0.735789 0.654288
2 0.041366 0.211168 0.373776 0.764705
3 0.009699 0.786710 0.299999 0.805505
4 0.586881 0.150332 0.916509 0.710650
5 0.081958 0.019041 0.739192 0.351366
表结构如下:
Field Type Null Key
id_name bigint(20) YES MUL
col_1 double YES
col_2 double YES
col_3 double YES
col_4 double YES
见证奇迹的时刻:
show index in tosql_table;
居然默认创建成功了一个索引,索引名ix_tosql_table_id_name 。
索引字段是int ,执行append模式
respond = data.to_sql('tosql_table',engine,chunksize=1000,if_exists='append',index=True)
也居然默认创建成功了一个索引,索引名ix_tosql_table_id_name 。
3、index=True的bug
整理了一下思路,折腾一下午的问题:
to_sql函数index=True时,存在一个bug 。
(1)append时
- 如果索引字段是TEXT类型,不报错,数据导入,在表上不创建索引。
- 如果索引字段是int类型,正常导入数据,表上也有默认索引。
(2)replace时
- 如果索引字段是TEXT类型,报错,原因是在表上创建索引失败,只能创建表结构,不能导入数据。
- 如果索引字段是int类型,正常导入数据,表上也有默认索引。
(3)版本
print(pd.__version__)
print(np.__version__)
2.0.0
1.23.5
MySQL版本8.0.16