Dask read_sql_table读取含NUMERIC类型日期的SQLite表失败求解决
这个问题的核心在于SQLAlchemy对SQLite NUMERIC类型的默认映射逻辑:SQLite本身是动态类型,但SQLAlchemy会把NUMERIC列默认解析为Decimal类型,而你的列里实际存的是ISO格式的字符串,这就导致读取时触发了字符串转Decimal的类型错误——Pandas的read_sql_table可能在底层做了特殊处理先按字符串读取,再执行日期解析,但Dask的实现直接依赖SQLAlchemy的类型映射,所以报错了。
下面是几个可行的解决办法:
方法一:强制指定列的SQLAlchemy类型
在调用ddf.read_sql_table时,通过dtype参数把datetime列强制指定为TEXT类型,让SQLAlchemy按字符串读取,之后再解析为日期:
import sqlalchemy import dask.dataframe as ddf from sqlalchemy.types import TEXT connString = "sqlite:///c:\\temp\\test.db" engine = sqlalchemy.create_engine(connString) # 读取时指定datetime列按TEXT类型读取 daskDF = ddf.read_sql_table( 'testtable', connString, index_col='uid', dtype={'datetime': TEXT}, # 关键:强制类型为TEXT parse_dates={'datetime': '%Y-%m-%d %H:%M:%S'} ) # 验证结果 daskDF.compute()
这个方法最直接,不需要修改数据库或写复杂SQL,只是告诉SQLAlchemy不要把该列当成数值类型处理。
方法二:自定义SQL查询,CAST列类型
如果不想依赖类型指定,可以直接写SQL查询,把NUMERIC列转成TEXT后再让Dask读取:
import dask.dataframe as ddf connString = "sqlite:///c:\\temp\\test.db" query = "SELECT uid, CAST(datetime AS TEXT) AS datetime FROM testtable" # 用read_sql_query读取自定义查询结果 daskDF = ddf.read_sql_query( query, connString, index_col='uid', parse_dates={'datetime': '%Y-%m-%d %H:%M:%S'} ) daskDF.compute()
这种方式更灵活,适合需要对原始数据做预处理的场景,缺点是需要手动编写查询语句。
方法三:修改SQLAlchemy的类型映射(全局/临时)
如果有多个表需要处理,可以临时修改SQLAlchemy对SQLite NUMERIC类型的映射规则,不过这个方法要谨慎,避免影响其他列的处理:
from sqlalchemy.dialects.sqlite import base as sqlite_base # 临时替换NUMERIC类型的默认处理为TEXT original_numeric = sqlite_base.SQLITE_TYPE_MAP['NUMERIC'] sqlite_base.SQLITE_TYPE_MAP['NUMERIC'] = TEXT # 读取表 daskDF = ddf.read_sql_table( 'testtable', connString, index_col='uid', parse_dates={'datetime': '%Y-%m-%d %H:%M:%S'} ) # 恢复原始映射,避免影响其他操作 sqlite_base.SQLITE_TYPE_MAP['NUMERIC'] = original_numeric daskDF.compute()
这个方法适合批量处理多个表的场景,但注意要记得恢复原始映射,否则后续其他NUMERIC列的读取会受影响。
补充说明
你提到的SQLAlchemy直接查询也报错的情况,确实是因为SQLAlchemy对SQLite NUMERIC类型的解析逻辑问题——它会尝试把存储的字符串转成Decimal,导致TypeError: must be real number, not str。上面的方法本质上都是绕开这个错误的类型解析,让数据先以字符串形式读取,再转成日期类型。
内容的提问来源于stack exchange,提问作者egold

