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 提供的DateTimesqlite.DATETIME 等类型,则使用 ISO 格式存储值,这导致 TEXT 存储类:docs.sqlalchemy.org/en/latest/dialects/…。即使 DATETIME 具有 NUMERIC 亲和力。

以上是关于SQLAlchemy 选择在 SQLite 表上给出不同的结果,原始 sql 与可选的主要内容,如果未能解决你的问题,请参考以下文章

从 sqlite3 数据库上的表单提交中的 sqlalchemy 行更新行为不一致

SQLAlchemy:pool_size 和 SQLite

在 4 个表上的 sqlite 中完全外连接

使用 SQLAlchemy 过滤今天在 SQLite 中提交的记录

Python3+SQLAlchemy+Sqlite3实现ORM教程

为什么(以及如何?)sqlalchemy使用sqlite绕过FK约束? [重复]