在使用pandas和dask读取SQLite表时,当从SQLite表中选择存储为NUMERIC数据类型的日期时间(ISO格式化字符串)时,我遇到了SQLAlchemy的一些意外行为。 SQLAlchemy原始SQL查询工作正常,但使用从反射构造的可选择的查询失败。这两个查询看起来是等效的。
我在下面粘贴了一个示例,以及回溯。有人可以解释示例中第三个查询的错误吗?
使用NUMERIC datetime:
设置表格import sqlalchemy as sa
from sqlalchemy import text
connString = "sqlite:///c:\\temp\\test.db"
engine = sa.create_engine(connString)
conn = engine.connect()
conn.execute("create table testtable (uid INTEGER Primary Key, datetime NUMERIC)")
conn.execute("insert into testtable values (1, '2017-08-03 01:11:31')")
print(conn.execute('PRAGMA table_info(testtable)').fetchall())
# [(0, 'uid', 'INTEGER', 0, None, 1), (1, 'datetime', 'NUMERIC', 0, None, 0)]
使用原始SQL工作查询:
resultList1 = conn.execute("SELECT testtable.uid, testtable.datetime \nFROM testtable").fetchall()
print(resultList1)
# [(1, '2017-08-03 01:11:31')]
使用此可选作品进行查询:
resultList2 = conn.execute(sa.sql.select(columns=[text('uid'),text('datetime')]).select_from(text('testtable'))).fetchall()
print(resultList2)
# [(1, '2017-08-03 01:11:31')]
使用此可选择的查询失败:
m = sa.MetaData()
table = sa.Table('testtable', m, autoload=True, autoload_with=engine)
selectble = sa.sql.select(table.columns).select_from(table)
print(selectble.compile().string)
# note: same raw sql query as above
# "SELECT testtable.uid, testtable.datetime \nFROM testtable"
resultList3 = conn.execute(sa.sql.select(table.columns).select_from(table)).fetchall()
# SAWarning: Dialect sqlite+pysqlite does *not* support Decimal objects natively...
print(resultList3)
conn.close()
错误:
Traceback (most recent call last):
File "<ipython-input-20-188c84a35d95>", line 1, in <module>
print(resultList3)
File "c:\program files\python36\lib\site-packages\sqlalchemy\engine\result.py", line 156, in __repr__
return repr(sql_util._repr_row(self))
File "c:\program files\python36\lib\site-packages\sqlalchemy\sql\util.py", line 329, in __repr__
", ".join(trunc(value) for value in self.row),
TypeError: must be real number, not str
答案 0 :(得分:1)
SQLite与大多数SQL数据库的类型系统非常不同:它使用dynamic typing,在conversion后,您为列提供的类型名称确定其affinity,例如NUMERIC:
具有NUMERIC亲和力的列可能包含使用所有五个存储类的值。当文本数据插入NUMERIC列时,如果此类转换是无损且可逆的,则文本的存储类将转换为INTEGER或REAL(按优先顺序)。对于TEXT和REAL存储类之间的转换,如果保留数字的前15个有效十进制数字,SQLite认为转换是无损且可逆的。如果无法将TEXT无损转换为INTEGER或REAL,则使用TEXT存储类存储该值。不会尝试转换NULL或BLOB值。
由于您已插入无法进行(无损)转换为INTEGER或REAL 1 的值,因此您的值使用TEXT storage class,并且SQLAlchemy / pysqlite不满意另一方面,it can convert to float
的期望值为fails。
键入系统会导致其他类似的问题,例如,当使用DATETIME typename从表中反映SELECT CREATE TABLE ... AS的结果表时,该表将转换为NUMERIC亲和力。
演示此问题的简短代码示例:
In [2]: foo = Table('foo', metadata, Column('bar', NUMERIC))
In [3]: foo.create(engine)
CREATE TABLE foo (
bar NUMERIC
)
In [4]: engine.execute("insert into foo values ('not really a number, no')")
Out[4]: <sqlalchemy.engine.result.ResultProxy at 0x7fbcd7ee8f98>
In [5]: foo.select().execute().fetchall()
Out[5]: ---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
...
~/Work/SO/lib/python3.6/site-packages/sqlalchemy/sql/util.py in __repr__(self)
327 trunc = self.trunc
328 return "(%s%s)" % (
--> 329 ", ".join(trunc(value) for value in self.row),
330 "," if len(self.row) == 1 else ""
331 )
TypeError: must be real number, not str
1 可能是sqlite + pysqlite方言本身不支持Decimal
的原因 - neither does SQLite