SQLAlchemy 选择在 SQLite 表上给出不同的结果,原始 sql 与可选
Posted
技术标签:
【中文标题】SQLAlchemy 选择在 SQLite 表上给出不同的结果,原始 sql 与可选【英文标题】:SQLAlchemy selects give different results on SQLite table, raw sql versus selectable 【发布时间】:2018-07-05 21:09:49 【问题描述】:在使用 pandas 和 dask 读取 SQLite 表时,我在从存储为 NUMERIC 数据类型的日期时间(ISO 格式字符串)的 SQLite 表中进行选择时遇到了 SQLAlchemy 的一些意外行为。 SQLAlchemy 原始 SQL 查询工作正常,但使用从反射构造的可选查询失败。这两个查询似乎是等价的。
我在下面粘贴了一个示例以及回溯。有人可以解释示例中的第三个查询有什么问题吗?
使用 NUMERIC 日期时间设置表:
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
【问题讨论】:
相关:***.com/questions/35983172/… 【参考方案1】:SQLite 与大多数 SQL 数据库有一个非常不同的类型系统:它使用dynamic typing,在conversion 之后,您给列指定的类型名称决定了它的affinity,例如NUMERIC:
具有 NUMERIC 亲和性的列可能包含使用所有五个存储类的值。将文本数据插入 NUMERIC 列时,如果这种转换是无损且可逆的,则文本的存储类将转换为 INTEGER 或 REAL(按优先顺序)。对于 TEXT 和 REAL 存储类之间的转换,如果保留数字的前 15 位有效十进制数字,则 SQLite 认为转换是无损和可逆的。如果无法将 TEXT 无损转换为 INTEGER 或 REAL,则 使用 TEXT 存储类存储值。不会尝试转换 NULL 或 BLOB 值。
由于您已插入无法(无损)转换为 INTEGER 或 REAL1 的值,因此您的值使用 TEXT storage class,并且 SQLAlchemy/pysqlite 不满意,因为它在另一方面期望值it can convert to float
,fails。
键入系统会导致其他类似问题,例如在使用 DATETIME 类型名(已转换为 NUMERIC 关联性)的表中针对 SELECT 反映来自 CREATE TABLE ... AS 的结果表时。
演示该问题的较短代码示例:
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
【讨论】:
感谢您的回答。实际上,我正在使用的具有 NUMERIC 日期时间的实际 SQLite 表是由 Oracle 数据库表的 SQLAlchemy 反射创建的。当从 Oracle 中选择数据并将其插入 SQLite 表时,SQLAlchemy 会自动将日期时间(准确地说是 Oracle 日期)转换为 NUMERIC 类型。那么,这引出了另一个问题:是否可以强制 SQLAlchemy 将日期时间转换为 SQLite TEXT 类型? 如果您在定义模型时使用 SQLAlchemy 提供的DateTime
或sqlite.DATETIME
等类型,则使用 ISO 格式存储值,这导致 TEXT 存储类:docs.sqlalchemy.org/en/latest/dialects/…。即使 DATETIME 具有 NUMERIC 亲和力。以上是关于SQLAlchemy 选择在 SQLite 表上给出不同的结果,原始 sql 与可选的主要内容,如果未能解决你的问题,请参考以下文章
从 sqlite3 数据库上的表单提交中的 sqlalchemy 行更新行为不一致
使用 SQLAlchemy 过滤今天在 SQLite 中提交的记录