Pandas的to_sql函数使用索引的坑

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:[email protected]: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

猜你喜欢

转载自blog.csdn.net/qq_39065491/article/details/131476542