SqlAlchemy-2-0-中文文档-三十-

SqlAlchemy 2.0 中文文档(三十)

原文:docs.sqlalchemy.org/en/20/contents.html

MySQL 和 MariaDB

原文:docs.sqlalchemy.org/en/20/dialects/mysql.html

支持 MySQL / MariaDB 数据库。

以下表总结了数据库发布版本的当前支持级别。

支持的 MySQL / MariaDB 版本

支持类型 版本
CI 中完全测试 5.6, 5.7, 8.0 / 10.8, 10.9
正常支持 5.6+ / 10+
尽力而为 5.0.2+ / 5.0.2+

DBAPI 支持

提供以下方言/DBAPI 选项。请参考各自的 DBAPI 部分以获取连接信息。

  • mysqlclient(MySQL-Python 的维护分支)

  • PyMySQL

  • MariaDB Connector/Python

  • MySQL Connector/Python

  • asyncmy

  • aiomysql

  • CyMySQL

  • PyODBC

支持的版本和功能

SQLAlchemy 支持从版本 5.0.2 开始的 MySQL,以及所有现代版本的 MariaDB。有关任何给定服务器版本支持的功能的详细信息,请参阅官方 MySQL 文档。

从版本 1.4 开始更改:最低支持的 MySQL 版本现在是 5.0.2。

MariaDB 支持

MySQL 的 MariaDB 变体保留了与 MySQL 协议的基本兼容性,但这两个产品的发展仍在分歧。在 SQLAlchemy 领域,这两个数据库有一小部分语法和行为上的差异,SQLAlchemy 会自动适应。要连接到 MariaDB 数据库,不需要对数据库 URL 进行任何更改:

engine = create_engine("mysql+pymysql://user:pass@some_mariadb/dbname?charset=utf8mb4")

在首次连接时,SQLAlchemy 方言采用服务器版本检测方案,确定后端数据库是否报告为 MariaDB。根据此标志,方言可以在其行为必须不同的领域做出不同选择。

仅 MariaDB 模式

该方言还支持一个可选的“仅 MariaDB”连接模式,这对于应用程序使用 MariaDB 特定功能且与 MySQL 数据库不兼容的情况可能很有用。要使用此操作模式,请将上述 URL 中的“mysql”标记替换为“mariadb”:

engine = create_engine("mariadb+pymysql://user:pass@some_mariadb/dbname?charset=utf8mb4")

上述引擎在首次连接时,如果服务器版本检测检测到后端数据库不是 MariaDB,则会引发错误。

当使用以"mariadb"作为方言名称的引擎时,所有包含“mysql”名称的 mysql 特定选项现在都以"mariadb"命名。这意味着选项如mysql_engine应该命名为mariadb_engine,等等。对于同时使用“mysql”和"mariadb"方言的应用程序,可以同时使用“mysql”和"mariadb"选项:

my_table = Table(
    "mytable",
    metadata,
    Column("id", Integer, primary_key=True),
    Column("textdata", String(50)),
    mariadb_engine="InnoDB",
    mysql_engine="InnoDB",
)

Index(
    "textdata_ix",
    my_table.c.textdata,
    mysql_prefix="FULLTEXT",
    mariadb_prefix="FULLTEXT",
)

当上述结构被反映时,将发生类似的行为,即当数据库 URL 基于“mariadb”名称时,“mariadb”前缀将存在于选项名称中。

版本 1.4 中的新功能:添加了支持“MariaDB-only mode”的“mariadb”方言名称,用于 MySQL 方言。 ## 连接超时和断开连接

MySQL / MariaDB 具有自动连接关闭行为,对于空闲时间超过固定时间的连接,默认为 8 小时。为了避免出现此问题,使用create_engine.pool_recycle选项,该选项确保如果连接在池中存在了固定秒数,则该连接将被丢弃并替换为新连接:

engine = create_engine('mysql+mysqldb://...', pool_recycle=3600)

为了更全面地检测连接池中的断开连接,包括适应服务器重启和网络问题,可以采用预先 ping 的方法。请参阅处理断开连接了解当前的方法。

另请参阅

处理断开连接 - 关于处理超时连接以及数据库重新启动的几种技术的背景。 ## 包括存储引擎的 CREATE TABLE 参数

MySQL 和 MariaDB 的 CREATE TABLE 语法都包括各种特殊选项,包括ENGINECHARSETMAX_ROWSROW_FORMATINSERT_METHOD等等。为了适应这些参数的渲染,指定形式mysql_argument_name="value"。例如,要指定一个具有ENGINEInnoDBCHARSETutf8mb4KEY_BLOCK_SIZE1024的表:

Table('mytable', metadata,
      Column('data', String(32)),
      mysql_engine='InnoDB',
      mysql_charset='utf8mb4',
      mysql_key_block_size="1024"
     )

当支持仅 MariaDB 模式时,还必须包含相同的“mariadb”前缀下的键。这些值当然可以独立变化,以便在 MySQL 和 MariaDB 上保持不同的设置:

# support both "mysql" and "mariadb-only" engine URLs

Table('mytable', metadata,
      Column('data', String(32)),

      mysql_engine='InnoDB',
      mariadb_engine='InnoDB',

      mysql_charset='utf8mb4',
      mariadb_charset='utf8',

      mysql_key_block_size="1024"
      mariadb_key_block_size="1024"

     )

MySQL / MariaDB 方言通常将任何指定为mysql_keyword_name的关键字转换为CREATE TABLE语句中的KEYWORD_NAME。其中一些名称将以空格而不是下划线呈现;为了支持此,MySQL 方言具有对这些特定名称的意识,其中包括DATA DIRECTORY(例如mysql_data_directory)、CHARACTER SET(例如mysql_character_set)和INDEX DIRECTORY(例如mysql_index_directory)。

最常见的参数是 mysql_engine,它指的是表的存储引擎。从历史上看,MySQL 服务器安装会将此值默认为 MyISAM,尽管较新版本可能将默认值设置为 InnoDBInnoDB 引擎通常更受欢迎,因为它支持事务和外键。

在使用 MyISAM 存储引擎创建的 MySQL / MariaDB 数据库中创建的 Table 实际上是非事务性的,这意味着对该表的任何 INSERT/UPDATE/DELETE 语句都将被调用为自动提交。它也不支持外键约束;虽然 CREATE TABLE 语句接受外键选项,但在使用 MyISAM 存储引擎时,这些参数将被丢弃。反映这样的表也不会产生外键约束信息。

对于完全原子事务以及对外键约束的支持,所有参与的 CREATE TABLE 语句必须指定事务引擎,在绝大多数情况下是 InnoDB

大小写敏感性和表反射

MySQL 和 MariaDB 都对大小写敏感的标识符名称提供不一致的支持,其支持基于底层操作系统的具体细节。但是,已经观察到无论存在何种大小写敏感性行为,外键声明中的表名称总是以全小写形式从数据库接收,这使得准确反映使用混合大小写标识符名称的相互关联表的架构成为不可能。

因此,强烈建议在 SQLAlchemy 中以及在 MySQL / MariaDB 数据库本身中将表名声明为全小写,特别是如果要使用数据库反射功能的话。

事务隔离级别

所有 MySQL / MariaDB 方言都支持通过方言特定参数 create_engine.isolation_level(由 create_engine() 接受)以及作为传递给 Connection.execution_options() 的参数的 Connection.execution_options.isolation_level 参数来设置事务隔离级别。此功能通过为每个新连接发出命令 SET SESSION TRANSACTION ISOLATION LEVEL <level> 来工作。对于特殊的 AUTOCOMMIT 隔离级别,使用了 DBAPI 特定的技术。

使用 create_engine() 设置隔离级别:

engine = create_engine(
                "mysql+mysqldb://scott:tiger@localhost/test",
                isolation_level="READ UNCOMMITTED"
            )

通过每个连接执行选项进行设置:

connection = engine.connect()
connection = connection.execution_options(
    isolation_level="READ COMMITTED"
)

isolation_level的有效值包括:

  • READ COMMITTED

  • READ UNCOMMITTED

  • REPEATABLE READ

  • SERIALIZABLE

  • AUTOCOMMIT

特殊的AUTOCOMMIT值利用特定 DBAPI 提供的各种“autocommit”属性,并且目前受到 MySQLdb、MySQL-Client、MySQL-Connector Python 和 PyMySQL 的支持。使用它,数据库连接将返回SELECT @@autocommit;的值为 true。

还有更多隔离级别配置选项,例如与主Engine关联的“子引擎”对象,每个对象应用不同的隔离级别设置。有关详情,请参阅设置事务隔离级别,包括 DBAPI 自动提交。

另请参见

设置事务隔离级别,包括 DBAPI 自动提交

AUTO_INCREMENT 行为

在创建表时,SQLAlchemy 将自动在第一个未标记为外键的Integer主键列上设置AUTO_INCREMENT

>>> t = Table('mytable', metadata,
...   Column('mytable_id', Integer, primary_key=True)
... )
>>> t.create()
CREATE TABLE mytable (
 id INTEGER NOT NULL AUTO_INCREMENT,
 PRIMARY KEY (id)
)

您可以通过将False传递给Column.autoincrement参数的Column来禁用此行为。此标志也可用于在某些存储引擎中为多列键的次要列启用自动增量:

Table('mytable', metadata,
      Column('gid', Integer, primary_key=True, autoincrement=False),
      Column('id', Integer, primary_key=True)
     )

服务器端游标

mysqlclient、PyMySQL、mariadbconnector 方言支持服务器端游标,并且可能也适用于其他方言。这可以通过使用“buffered=True/False”标志(如果可用)或通过在内部使用类似于MySQLdb.cursors.SSCursorpymysql.cursors.SSCursor的类来实现。

服务器端游标可以通过使用Connection.execution_options.stream_results连接执行选项来启用每个语句的。

with engine.connect() as conn:
    result = conn.execution_options(stream_results=True).execute(text("select * from table"))

请注意,某些类型的 SQL 语句可能不支持使用服务器端游标;通常,只应该使用返回行的 SQL 语句与此选项一起使用。

自版本 1.4 起弃用:dialect-level server_side_cursors 标志已弃用,并将在将来的版本中删除。请使用Connection.stream_results执行选项来支持无缓冲游标。

另请参见

使用服务器端游标(也称为流式结果) ## Unicode

字符集选择

大多数 MySQL / MariaDB DBAPI 都提供了为连接设置客户端字符集的选项。通常可以使用 URL 中的charset参数来实现,例如:

e = create_engine(
    "mysql+pymysql://scott:tiger@localhost/test?charset=utf8mb4")

这个字符集是连接的客户端字符集。一些 MySQL DBAPI 会将其默认为诸如latin1之类的值,而一些则会使用my.cnf文件中的default-character-set设置。应该查阅所使用的 DBAPI 的文档以获取具体行为。

用于 Unicode 的编码传统上一直是'utf8'。然而,从 MySQL 版本 5.5.3 和 MariaDB 5.5 开始,引入了一个新的 MySQL 特定编码'utf8mb4',而且自 MySQL 8.0 起,如果在任何服务器端指令中指定了纯utf8,服务器会发出警告,并用utf8mb3替换。之所以使用这种新编码的原因是因为 MySQL 的传统 utf-8 编码只支持三字节的代码点而不是四字节。因此,在与包含超过三字节大小的代码点的 MySQL 或 MariaDB 数据库通信时,如果数据库和客户端 DBAPI 都支持,首选使用这种新的字符集,如下所示:

e = create_engine(
    "mysql+pymysql://scott:tiger@localhost/test?charset=utf8mb4")

所有现代的 DBAPI 都应支持utf8mb4字符集。

为了对使用了传统utf8的模式使用utf8mb4编码,可能需要对 MySQL/MariaDB 模式和/或服务器配置进行更改。

另请参阅

utf8mb4 字符集 - 在 MySQL 文档中

处理二进制数据警告和 Unicode

在写作本文时,MySQL 版本 5.6、5.7 和以后版本(不包括 MariaDB)现在在尝试将二进制数据传递到数据库时会发出警告,同时也设置了字符集编码,但是二进制数据本身对于该编码来说不合法:

default.py:509: Warning: (1300, "Invalid utf8mb4 character string:
'F9876A'")
  cursor.execute(statement, parameters)

此警告是由于 MySQL 客户端库尝试将二进制字符串解释为 Unicode 对象,即使使用了诸如LargeBinary这样的数据类型也是如此。要解决此问题,SQL 语句在任何呈现如下的非 NULL 值之前都需要存在一个二进制的“字符集介绍”:

INSERT INTO table (data) VALUES (_binary %s)

这些字符集介绍由 DBAPI 驱动程序提供,假设使用了 mysqlclient 或 PyMySQL(两者都是推荐的)。将查询字符串参数binary_prefix=true添加到 URL 中以修复此警告:

# mysqlclient
engine = create_engine(
    "mysql+mysqldb://scott:tiger@localhost/test?charset=utf8mb4&binary_prefix=true")

# PyMySQL
engine = create_engine(
    "mysql+pymysql://scott:tiger@localhost/test?charset=utf8mb4&binary_prefix=true")

binary_prefix标志可能受到其他 MySQL 驱动程序的支持与否的影响。

SQLAlchemy 本身无法可靠地渲染这个_binary前缀,因为它不适用于 NULL 值,而绑定参数时 NULL 值是有效的。由于 MySQL 驱动程序将参数直接渲染到 SQL 字符串中,这是传递此附加关键字的最有效位置。

另请参阅

字符集介绍 - 在 MySQL 网站上

ANSI 引用风格

MySQL / MariaDB 具有两种标识符“引用风格”,一种使用反引号,另一种使用引号,例如 some_identifier vs. "some_identifier"。所有 MySQL 方言在首次使用特定 Engine 建立连接时,通过检查 sql_mode 的值来检测使用的版本。此引用风格在呈现表和列名称以及反映现有数据库结构时起作用。检测完全是自动的,不需要任何特殊配置来使用任一引用风格。

更改 sql_mode

MySQL 支持在多个 服务器 SQL 模式下运行,对于服务器和客户端都是如此。要为给定应用程序更改 sql_mode,开发人员可以利用 SQLAlchemy 的事件系统。

在以下示例中,事件系统用于在 first_connectconnect 事件上设置 sql_mode

from sqlalchemy import create_engine, event

eng = create_engine("mysql+mysqldb://scott:tiger@localhost/test", echo='debug')

# `insert=True` will ensure this is the very first listener to run
@event.listens_for(eng, "connect", insert=True)
def connect(dbapi_connection, connection_record):
    cursor = dbapi_connection.cursor()
    cursor.execute("SET sql_mode = 'STRICT_ALL_TABLES'")

conn = eng.connect()

在上面示例中,当特定的 DBAPI 连接首次为给定的连接池创建时,“connect”事件将在连接可供连接池使用之前在连接上调用“SET”语句。此外,因为函数被注册为 insert=True,它将被添加到已注册函数的内部列表之前。

MySQL / MariaDB SQL 扩展

许多 MySQL / MariaDB 的 SQL 扩展都通过 SQLAlchemy 的通用函数和操作符支持:

table.select(table.c.password==func.md5('plaintext'))
table.select(table.c.username.op('regexp')('^[a-d]'))

当然,任何有效的 SQL 语句也可以作为字符串执行。

目前可以直接支持一些有限的 MySQL / MariaDB 对 SQL 的扩展。

  • INSERT..ON DUPLICATE KEY UPDATE:参见 INSERT…ON DUPLICATE KEY UPDATE (Upsert)

  • SELECT 命令,使用Select.prefix_with()Query.prefix_with()

    select(...).prefix_with(['HIGH_PRIORITY', 'SQL_SMALL_RESULT'])
    
  • 带有 LIMIT 的 UPDATE:

    update(..., mysql_limit=10, mariadb_limit=10)
    
  • 优化器提示,使用Select.prefix_with()Query.prefix_with()

    select(...).prefix_with("/*+ NO_RANGE_OPTIMIZATION(t4 PRIMARY) */")
    
  • 索引提示,使用Select.with_hint()Query.with_hint()

    select(...).with_hint(some_table, "USE INDEX xyz")
    
  • MATCH 运算符支持:

    from sqlalchemy.dialects.mysql import match
    select(...).where(match(col1, col2, against="some expr").in_boolean_mode())
    
    .. seealso::
    
        :class:`_mysql.match`
    

INSERT/DELETE…RETURNING

MariaDB 方言支持 10.5+的INSERT..RETURNINGDELETE..RETURNING(10.0+)语法。在某些情况下,INSERT..RETURNING可以自动使用,以获取新生成的标识符,而不是使用cursor.lastrowid的传统方法,但是对于简单的单语句情况,目前仍更喜欢使用cursor.lastrowid,因为它的性能更好。

要在每个语句的基础上使用显式的RETURNING子句,请使用 _UpdateBase.returning()方法:

# INSERT..RETURNING
result = connection.execute(
    table.insert().
    values(name='foo').
    returning(table.c.col1, table.c.col2)
)
print(result.all())

# DELETE..RETURNING
result = connection.execute(
    table.delete().
    where(table.c.name=='foo').
    returning(table.c.col1, table.c.col2)
)
print(result.all())

版本 2.0 中的新功能:增加了 MariaDB RETURNING 支持

插入…在重复键更新时(Upsert)

MySQL / MariaDB 允许通过 INSERT 语句的 ON DUPLICATE KEY UPDATE 子句将行“upserts”(更新或插入)到表中。只有在该行不匹配表中现有的主键或唯一键时,候选行才会被插入;否则,将执行更新。该语句允许分开指定要插入的值与要更新的值。

SQLAlchemy 通过 MySQL 特定的insert()函数提供ON DUPLICATE KEY UPDATE支持,该函数提供了生成方法Insert.on_duplicate_key_update()

>>> from sqlalchemy.dialects.mysql import insert

>>> insert_stmt = insert(my_table).values(
...     id='some_existing_id',
...     data='inserted value')

>>> on_duplicate_key_stmt = insert_stmt.on_duplicate_key_update(
...     data=insert_stmt.inserted.data,
...     status='U'
... )
>>> print(on_duplicate_key_stmt)
INSERT  INTO  my_table  (id,  data)  VALUES  (%s,  %s)
ON  DUPLICATE  KEY  UPDATE  data  =  VALUES(data),  status  =  %s 

不同于 PostgreSQL 的“ON CONFLICT”短语,"ON DUPLICATE KEY UPDATE"短语将始终匹配任何主键或唯一键,并且如果有匹配,将始终执行更新;它没有选项可以引发错误或跳过执行更新。

ON DUPLICATE KEY UPDATE用于对已存在的行执行更新,使用新值的任何组合以及提议插入的值。这些值通常使用传递给Insert.on_duplicate_key_update()的关键字参数指定为列键值(通常是列的名称,除非它指定了Column.key)作为键,字面值或 SQL 表达式作为值:

>>> insert_stmt = insert(my_table).values(
...          id='some_existing_id',
...          data='inserted value')

>>> on_duplicate_key_stmt = insert_stmt.on_duplicate_key_update(
...     data="some data",
...     updated_at=func.current_timestamp(),
... )

>>> print(on_duplicate_key_stmt)
INSERT  INTO  my_table  (id,  data)  VALUES  (%s,  %s)
ON  DUPLICATE  KEY  UPDATE  data  =  %s,  updated_at  =  CURRENT_TIMESTAMP 

UpdateBase.values()类似的方式,也接受其他参数形式,包括一个单一的字典:

>>> on_duplicate_key_stmt = insert_stmt.on_duplicate_key_update(
...     {"data": "some data", "updated_at": func.current_timestamp()},
... )

以及一个 2-tuple 列表,它将自动提供类似于参数有序更新描述的参数有序 UPDATE 语句的方式。与Update对象不同,不需要特殊标志来指定意图,因为在此上下文中的参数形式是清楚的:

>>> on_duplicate_key_stmt = insert_stmt.on_duplicate_key_update(
...     [
...         ("data", "some data"),
...         ("updated_at", func.current_timestamp()),
...     ]
... )

>>> print(on_duplicate_key_stmt)
INSERT  INTO  my_table  (id,  data)  VALUES  (%s,  %s)
ON  DUPLICATE  KEY  UPDATE  data  =  %s,  updated_at  =  CURRENT_TIMESTAMP 

版本 1.3 中的更改:支持 MySQL ON DUPLICATE KEY UPDATE 内的参数有序 UPDATE 子句

警告

Insert.on_duplicate_key_update() 方法 考虑 Python 端的默认 UPDATE 值或生成函数,例如使用 Column.onupdate 指定的值。这些值不会用于 ON DUPLICATE KEY 样式的 UPDATE,除非在参数中手动明确指定。

为了引用提议的插入行,Insert.inserted 特殊别名可作为 Insert 对象上的属性;此对象是一个 ColumnCollection,包含目标表的所有列:

>>> stmt = insert(my_table).values(
...     id='some_id',
...     data='inserted value',
...     author='jlh')

>>> do_update_stmt = stmt.on_duplicate_key_update(
...     data="updated value",
...     author=stmt.inserted.author
... )

>>> print(do_update_stmt)
INSERT  INTO  my_table  (id,  data,  author)  VALUES  (%s,  %s,  %s)
ON  DUPLICATE  KEY  UPDATE  data  =  %s,  author  =  VALUES(author) 

渲染时,“inserted” 命名空间将生成表达式 VALUES(<columnname>)

版本 1.2 中新增了对 MySQL ON DUPLICATE KEY UPDATE 子句的支持。

rowcount 支持

SQLAlchemy 将 DBAPI cursor.rowcount 属性标准化为“UPDATE 或 DELETE 语句匹配的行数”的通常定义。这与大多数 MySQL DBAPI 驱动程序的默认设置相矛盾,后者是“实际修改/删除的行数”。因此,SQLAlchemy MySQL 方言在连接时始终添加 constants.CLIENT.FOUND_ROWS 标志,或者在目标方言上等效的标志。这个设置目前是硬编码的。

另见

CursorResult.rowcount

MySQL / MariaDB 特定索引选项

可用于 MySQL 和 MariaDB 的 Index 构造的特定扩展。

索引长度

MySQL 和 MariaDB 都提供了一个选项,可以创建一定长度的索引条目,其中“长度”是指每个值中的字符数或字节数,这些值将成为索引的一部分。SQLAlchemy 通过 mysql_length 和/或 mariadb_length 参数提供了这个功能:

Index('my_index', my_table.c.data, mysql_length=10, mariadb_length=10)

Index('a_b_idx', my_table.c.a, my_table.c.b, mysql_length={'a': 4,
                                                           'b': 9})

Index('a_b_idx', my_table.c.a, my_table.c.b, mariadb_length={'a': 4,
                                                           'b': 9})

对于非二进制字符串类型,前缀长度以字符表示,对于二进制字符串类型,以字节表示。传递给关键字参数的值 必须 是整数(因此对索引的所有列都指定相同的前缀长度值)或字典,在字典中,键是列名,值是相应列的前缀长度值。MySQL 和 MariaDB 仅允许对索引的列指定长度,如果它是 CHAR、VARCHAR、TEXT、BINARY、VARBINARY 和 BLOB 类型的列。

索引前缀

MySQL 存储引擎允许在创建索引时指定索引前缀。SQLAlchemy 通过Indexmysql_prefix参数提供了这个功能:

Index('my_index', my_table.c.data, mysql_prefix='FULLTEXT')

传递给关键字参数的值将简单地传递给底层的 CREATE INDEX,因此它必须是您的 MySQL 存储引擎的有效索引前缀。

另请参阅

CREATE INDEX - MySQL 文档

索引类型

一些 MySQL 存储引擎允许在创建索引或主键约束时指定索引类型。SQLAlchemy 通过Indexmysql_using参数提供了这个功能:

Index('my_index', my_table.c.data, mysql_using='hash', mariadb_using='hash')

以及PrimaryKeyConstraint上的mysql_using参数:

PrimaryKeyConstraint("data", mysql_using='hash', mariadb_using='hash')

传递给关键字参数的值将简单地传递给底层的 CREATE INDEX 或 PRIMARY KEY 子句,因此它必须是您的 MySQL 存储引擎的有效索引类型。

更多信息请查看:

dev.mysql.com/doc/refman/5.0/en/create-index.html

dev.mysql.com/doc/refman/5.0/en/create-table.html

索引解析器

MySQL 中的 CREATE FULLTEXT INDEX 也支持“WITH PARSER”选项。可以使用关键字参数mysql_with_parser来实现:

Index(
    'my_index', my_table.c.data,
    mysql_prefix='FULLTEXT', mysql_with_parser="ngram",
    mariadb_prefix='FULLTEXT', mariadb_with_parser="ngram",
)

1.3 版本中的新功能 ## MySQL / MariaDB 外键

MySQL 和 MariaDB 关于外键的行为有一些重要的注意事项。

避免使用的外键参数

MySQL 和 MariaDB 都不支持外键参数“DEFERRABLE”、“INITIALLY”或“MATCH”。在ForeignKeyConstraintForeignKey中使用deferrableinitially关键字参数将导致这些关键字在 DDL 表达式中被渲染,然后在 MySQL 或 MariaDB 上引发错误。为了在外键上使用这些关键字,同时在 MySQL / MariaDB 后端上忽略它们,可以使用自定义编译规则:

from sqlalchemy.ext.compiler import compiles
from sqlalchemy.schema import ForeignKeyConstraint

@compiles(ForeignKeyConstraint, "mysql", "mariadb")
def process(element, compiler, **kw):
    element.deferrable = element.initially = None
    return compiler.visit_foreign_key_constraint(element, **kw)

“MATCH”关键字实际上更加隐蔽,而且在与 MySQL 或 MariaDB 后端一起使用时,SQLAlchemy 明确禁止使用。这个参数在 MySQL / MariaDB 中被静默忽略,但另外的效果是 ON UPDATE 和 ON DELETE 选项也被后端忽略。因此,不应该在 MySQL / MariaDB 后端使用 MATCH;与 DEFERRABLE 和 INITIALLY 一样,可以使用自定义编译规则来在 DDL 定义时纠正 ForeignKeyConstraint。

外键约束的反射

并非所有 MySQL / MariaDB 存储引擎都支持外键。在使用非常常见的 MyISAM MySQL 存储引擎时,通过表反射加载的信息将不包括外键。对于这些表,您可以在反射时提供一个 ForeignKeyConstraint

Table('mytable', metadata,
      ForeignKeyConstraint(['other_id'], ['othertable.other_id']),
      autoload_with=engine
     )

另请参阅

包括存储引擎的 CREATE TABLE 参数 ## MySQL / MariaDB 唯一约束和反射

SQLAlchemy 支持带有标志 unique=TrueIndex 构造,表示唯一索引,以及表示唯一约束的 UniqueConstraint 构造。在创建这些约束时,MySQL / MariaDB 支持这两种对象/语法。但是,MySQL / MariaDB 没有一个独立于唯一索引的唯一约束构造;也就是说,在 MySQL / MariaDB 上,“UNIQUE” 约束等同于创建一个“UNIQUE INDEX”。

在反射这些构造时,Inspector.get_indexes()Inspector.get_unique_constraints() 方法都会在 MySQL / MariaDB 中为唯一索引返回一个条目。然而,在使用 Table(..., autoload_with=engine) 执行完整表反射时,UniqueConstraint 构造在任何情况下都不是完全反映的 Table 构造的一部分;这个构造总是由 Table.indexes 集合中存在 unique=True 设置的 Index 表示。

TIMESTAMP / DATETIME 问题

为 MySQL / MariaDB 的 explicit_defaults_for_timestamp 启用 ON UPDATE CURRENT TIMESTAMP 渲染

MySQL / MariaDB 在历史上将 TIMESTAMP 数据类型的 DDL 扩展为短语“TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP”,其中包含非标准 SQL,当发生 UPDATE 时自动使用当前时间戳更新列,消除了在需要服务器端更新更改的情况下使用触发器的常规需求。

MySQL 5.6 引入了一个新的标志 explicit_defaults_for_timestamp,它禁用了上述行为,在 MySQL 8 中,此标志默认为 true,这意味着为了获得 MySQL 的“更新时间戳”,而不改变此标志,上述 DDL 必须显式地呈现。此外,相同的 DDL 对于 DATETIME 数据类型也是有效的。

SQLAlchemy 的 MySQL 方言尚未提供生成 MySQL 的“ON UPDATE CURRENT_TIMESTAMP”子句的选项,注意这不是通用的“ON UPDATE”,因为标准 SQL 中没有这样的语法。SQLAlchemy 的 Column.server_onupdate 参数目前与此特殊的 MySQL 行为无关。

要生成此 DDL,请使用 Column.server_default 参数,并传递一个包含 ON UPDATE 子句的文本子句:

from sqlalchemy import Table, MetaData, Column, Integer, String, TIMESTAMP
from sqlalchemy import text

metadata = MetaData()

mytable = Table(
    "mytable",
    metadata,
    Column('id', Integer, primary_key=True),
    Column('data', String(50)),
    Column(
        'last_updated',
        TIMESTAMP,
        server_default=text("CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP")
    )
)

相同的说明适用于使用 DateTimeDATETIME 数据类型:

from sqlalchemy import DateTime

mytable = Table(
    "mytable",
    metadata,
    Column('id', Integer, primary_key=True),
    Column('data', String(50)),
    Column(
        'last_updated',
        DateTime,
        server_default=text("CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP")
    )
)

即使 Column.server_onupdate 功能不生成此 DDL,但仍然有可能希望向 ORM 发出信号,表示应该获取此更新的值。此语法如下所示:

from sqlalchemy.schema import FetchedValue

class MyClass(Base):
    __tablename__ = 'mytable'

    id = Column(Integer, primary_key=True)
    data = Column(String(50))
    last_updated = Column(
        TIMESTAMP,
        server_default=text("CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP"),
        server_onupdate=FetchedValue()
    )
```  ### TIMESTAMP 列和 NULL

MySQL 历史上要求指定 TIMESTAMP 数据类型的列隐式包括默认值 CURRENT_TIMESTAMP,即使没有明确说明,并且另外将列设置为 NOT NULL,这与所有其他数据类型相反的行为:

```py
mysql> CREATE TABLE ts_test (
    -> a INTEGER,
    -> b INTEGER NOT NULL,
    -> c TIMESTAMP,
    -> d TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    -> e TIMESTAMP NULL);
Query OK, 0 rows affected (0.03 sec)

mysql> SHOW CREATE TABLE ts_test;
+---------+-----------------------------------------------------
| Table   | Create Table
+---------+-----------------------------------------------------
| ts_test | CREATE TABLE `ts_test` (
  `a` int(11) DEFAULT NULL,
  `b` int(11) NOT NULL,
  `c` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  `d` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `e` timestamp NULL DEFAULT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1

以上,我们看到一个 INTEGER 列默认为 NULL,除非指定为 NOT NULL。但是当列的类型为 TIMESTAMP 时,会生成一个隐式的默认值 CURRENT_TIMESTAMP,这也会强制使列成为 NOT NULL,即使我们没有这样指定。

MySQL 的这种行为可以通过 MySQL 方面的 explicit_defaults_for_timestamp 配置标志在 MySQL 5.6 中引入。启用此服务器设置后,TIMESTAMP 列在 MySQL 方面的默认值和可空性方面的行为类似于任何其他数据类型。

然而,为了适应大多数未指定此新标志的 MySQL 数据库,SQLAlchemy 在任何未指定 nullable=False 的 TIMESTAMP 列中都显式地发出“NULL”指示符。为了适应指定了 explicit_defaults_for_timestamp 的新数据库,SQLAlchemy 还为指定了 nullable=False 的 TIMESTAMP 列发出 NOT NULL。以下示例说明了:

from sqlalchemy import MetaData, Integer, Table, Column, text
from sqlalchemy.dialects.mysql import TIMESTAMP

m = MetaData()
t = Table('ts_test', m,
        Column('a', Integer),
        Column('b', Integer, nullable=False),
        Column('c', TIMESTAMP),
        Column('d', TIMESTAMP, nullable=False)
    )

from sqlalchemy import create_engine
e = create_engine("mysql+mysqldb://scott:tiger@localhost/test", echo=True)
m.create_all(e)

输出:

CREATE TABLE ts_test (
    a INTEGER,
    b INTEGER NOT NULL,
    c TIMESTAMP NULL,
    d TIMESTAMP NOT NULL
)

MySQL SQL 构造

对象名称 描述
match 生成一个 MATCH (X, Y) AGAINST ('TEXT') 子句。
class sqlalchemy.dialects.mysql.match

生成一个 MATCH (X, Y) AGAINST ('TEXT') 子句。

例如:

from sqlalchemy import desc
from sqlalchemy.dialects.mysql import match

match_expr = match(
    users_table.c.firstname,
    users_table.c.lastname,
    against="Firstname Lastname",
)

stmt = (
    select(users_table)
    .where(match_expr.in_boolean_mode())
    .order_by(desc(match_expr))
)

将生成类似于 SQL 的代码:

SELECT id, firstname, lastname
FROM user
WHERE MATCH(firstname, lastname) AGAINST (:param_1 IN BOOLEAN MODE)
ORDER BY MATCH(firstname, lastname) AGAINST (:param_2) DESC

match() 函数是所有 SQL 表达式可用的 ColumnElement.match() 方法的独立版本,与使用 ColumnElement.match() 时一样,但允许传递多个列。

参数:

  • cols – 要匹配的列表达式

  • against – 要比较的表达式

  • in_boolean_mode – 布尔值,将“布尔模式”设置为 true

  • in_natural_language_mode – 布尔值,将“自然语言”设置为 true

  • with_query_expansion – 布尔值,将“查询扩展”设置为 true

从版本 1.4.19 开始。

另请参阅

ColumnElement.match()

成员

in_boolean_mode(), in_natural_language_mode(), inherit_cache, with_query_expansion()

类签名

sqlalchemy.dialects.mysql.match (sqlalchemy.sql.expression.Generative, sqlalchemy.sql.expression.BinaryExpression)。

method in_boolean_mode() → Self

对 MATCH 表达式应用“IN BOOLEAN MODE”修饰符。

返回:

一个新的 match 实例,应用了修改。

method in_natural_language_mode() → Self

对 MATCH 表达式应用“IN NATURAL LANGUAGE MODE”修饰符。

返回:

一个新的 match 实例,应用了修改。

attribute inherit_cache: bool | None = True

指示此 HasCacheKey 实例是否应该使用其直接超类使用的缓存密钥生成方案。

此属性默认为 None,表示构造尚未考虑其是否适合参与缓存;这在功能上等同于将值设置为 False,但还会发出警告。

如果 SQL 与对象对应的属性不基于该类本身的属性而变化,并且不是基于其超类,则可以在特定类上设置此标志为True

另请参阅

启用自定义构造的缓存支持 - 设置第三方或用户定义的 SQL 构造的 HasCacheKey.inherit_cache 属性的一般指南。

method with_query_expansion() → Self

对 MATCH 表达式应用 “WITH QUERY EXPANSION” 修饰符。

返回:

一个具有应用修改的新 match 实例。

MySQL 数据类型

与所有 SQLAlchemy 方言一样,已知与 MySQL 兼容的所有大写类型都可以从顶级方言导入:

from sqlalchemy.dialects.mysql import (
    BIGINT,
    BINARY,
    BIT,
    BLOB,
    BOOLEAN,
    CHAR,
    DATE,
    DATETIME,
    DECIMAL,
    DECIMAL,
    DOUBLE,
    ENUM,
    FLOAT,
    INTEGER,
    LONGBLOB,
    LONGTEXT,
    MEDIUMBLOB,
    MEDIUMINT,
    MEDIUMTEXT,
    NCHAR,
    NUMERIC,
    NVARCHAR,
    REAL,
    SET,
    SMALLINT,
    TEXT,
    TIME,
    TIMESTAMP,
    TINYBLOB,
    TINYINT,
    TINYTEXT,
    VARBINARY,
    VARCHAR,
    YEAR,
)

特定于 MySQL 或具有 MySQL 特定构造参数的类型如下:

对象名称 描述
BIGINT MySQL BIGINTEGER 类型。
BIT MySQL BIT 类型。
CHAR MySQL CHAR 类型,用于固定长度的字符数据。
DATETIME MySQL DATETIME 类型。
DECIMAL MySQL DECIMAL 类型。
ENUM MySQL ENUM 类型。
FLOAT MySQL FLOAT 类型。
INTEGER MySQL INTEGER 类型。
JSON MySQL JSON 类型。
LONGBLOB MySQL LONGBLOB 类型,用于存储最多 2³² 字节的二进制数据。
LONGTEXT MySQL LONGTEXT 类型,用于存储编码长度达到 2³² 字节的字符数据。
MEDIUMBLOB MySQL MEDIUMBLOB 类型,用于存储最多 2²⁴ 字节的二进制数据。
MEDIUMINT MySQL MEDIUMINTEGER 类型。
MEDIUMTEXT MySQL MEDIUMTEXT 类型,用于存储编码长度达到 2²⁴ 字节的字符数据。
NCHAR MySQL NCHAR 类型。
NUMERIC MySQL NUMERIC 类型。
NVARCHAR MySQL NVARCHAR 类型。
REAL MySQL REAL 类型。
SET MySQL SET 类型。
SMALLINT MySQL SMALLINTEGER 类型。
TIME MySQL TIME 类型。
TIMESTAMP MySQL 的 TIMESTAMP 类型。
TINYBLOB MySQL 的 TINYBLOB 类型,用于最多 2⁸ 字节的二进制数据。
TINYINT MySQL 的 TINYINT 类型。
TINYTEXT MySQL 的 TINYTEXT 类型,用于最多 2⁸ 字节的字符存储。
VARCHAR MySQL 的 VARCHAR 类型,用于可变长度的字符数据。
YEAR MySQL 的 YEAR 类型,用于存储 1901-2155 年的单字节。
class sqlalchemy.dialects.mysql.BIGINT

MySQL 的 BIGINTEGER 类型。

成员

init()

类签名

sqlalchemy.dialects.mysql.BIGINTsqlalchemy.dialects.mysql.types._IntegerTypesqlalchemy.types.BIGINT

method __init__(display_width=None, **kw)

构造一个 BIGINTEGER。

参数:

  • display_width – 可选项,此数字的最大显示宽度。

  • unsigned – 一个布尔值,可选项。

  • zerofill – 可选项。如果为 true,则值将作为左填充零的字符串存储。请注意,这不会影响底层数据库 API 返回的值,它们仍然是数字。

class sqlalchemy.dialects.mysql.BINARY

SQL 的 BINARY 类型。

类签名

sqlalchemy.dialects.mysql.BINARYsqlalchemy.types._Binary

class sqlalchemy.dialects.mysql.BIT

MySQL 的 BIT 类型。

此类型适用于 MySQL 5.0.3 或更高版本的 MyISAM,以及 5.0.5 或更高版本的 MyISAM,MEMORY,InnoDB 和 BDB。对于较旧的版本,请使用 MSTinyInteger() 类型。

成员

init()

类签名

sqlalchemy.dialects.mysql.BITsqlalchemy.types.TypeEngine

method __init__(length=None)

构造一个 BIT。

参数:

length – 可选项,位数。

class sqlalchemy.dialects.mysql.BLOB

SQL 的 BLOB 类型。

类签名

sqlalchemy.dialects.mysql.BLOBsqlalchemy.types.LargeBinary

method __init__(length: int | None = None)

继承自 LargeBinary sqlalchemy.types.LargeBinary.__init__ 方法

构造一个 LargeBinary 类型。

参数:

length – 可选项,在 DDL 语句中用于列的长度,对于那些接受长度的二进制类型,比如 MySQL 的 BLOB 类型。

class sqlalchemy.dialects.mysql.BOOLEAN

SQL 的 BOOLEAN 类型。

类签名

sqlalchemy.dialects.mysql.BOOLEANsqlalchemy.types.Boolean

method __init__(create_constraint: bool = False, name: str | None = None, _create_events: bool = True, _adapted_from: SchemaType | None = None)

继承自 Boolean sqlalchemy.types.Boolean.__init__ 方法

构造一个布尔值。

参数:

  • create_constraint

    默认为 False。如果布尔值生成为 int/smallint,则还在表上创建一个 CHECK 约束,以确保值为 1 或 0。

    注意

    强烈建议 CHECK 约束具有显式名称,以支持模式管理问题。这可以通过设置 Boolean.name 参数或设置适当的命名约定来实现;有关背景信息,请参阅配置约束命名约定。

    从版本 1.4 开始更改:- 此标志现在默认为 False,意味着对非本地枚举类型不生成 CHECK 约束。

  • name – 如果生成 CHECK 约束,则指定约束的名称。

class sqlalchemy.dialects.mysql.CHAR

MySQL CHAR 类型,用于固定长度字符数据。

成员

init()

类签名

sqlalchemy.dialects.mysql.CHAR (sqlalchemy.dialects.mysql.types._StringType, sqlalchemy.types.CHAR)

method __init__(length=None, **kwargs)

构造一个 CHAR。

参数:

  • length – 最大数据长度,以字符为单位。

  • binary – 可选项,使用国家字符集的默认二进制排序。这不影响存储的数据类型,对于二进制数据,请使用 BINARY 类型。

  • collation – 可选项,请求特定的排序规则。必须与国家字符集兼容。

class sqlalchemy.dialects.mysql.DATE

SQL DATE 类型。

类签名

sqlalchemy.dialects.mysql.DATE (sqlalchemy.types.Date)

class sqlalchemy.dialects.mysql.DATETIME

MySQL DATETIME 类型。

成员

init()

类签名

sqlalchemy.dialects.mysql.DATETIME (sqlalchemy.types.DATETIME)

method __init__(timezone=False, fsp=None)

构造一个 MySQL DATETIME 类型。

参数:

  • timezone – MySQL 方言不使用。

  • fsp

    小数秒精度值。MySQL 5.6.4 支持存储小数秒;在为 DATETIME 类型生成 DDL 时将使用此参数。

    注意

    对于小数秒的 DBAPI 驱动程序支持可能有限;当前支持包括 MySQL Connector/Python。

class sqlalchemy.dialects.mysql.DECIMAL

MySQL DECIMAL 类型。

成员

init()

类签名

sqlalchemy.dialects.mysql.DECIMAL (sqlalchemy.dialects.mysql.types._NumericType, sqlalchemy.types.DECIMAL)

method __init__(precision=None, scale=None, asdecimal=True, **kw)

构造 DECIMAL。

参数:

  • precision – 此数字中的总位数。如果 scale 和 precision 都为 None,则值存储到服务器允许的限制。

  • scale – 小数点后的位数。

  • unsigned – 一个布尔值,可选的。

  • zerofill – 可选的。 如果为真,则值将作为左填充零的字符串存储。 请注意,这不影响底层数据库 API 返回的值,后者仍然是数字。

class sqlalchemy.dialects.mysql.DOUBLE

MySQL DOUBLE 类型。

类签名

sqlalchemy.dialects.mysql.DOUBLE (sqlalchemy.dialects.mysql.types._FloatType, sqlalchemy.types.DOUBLE)

method __init__(precision=None, scale=None, asdecimal=True, **kw)

构造一个 DOUBLE。

注意

DOUBLE 类型默认将浮点数转换为 Decimal,使用默认为 10 位的截断。 指定 scale=ndecimal_return_scale=n 以更改此比例,或指定 asdecimal=False 以直接将值返回为 Python 浮点数。

参数:

  • precision – 此数字中的总位数。 如果比例和精度都是无,则值将存储到服务器允许的限制。

  • scale – 小数点后的位数。

  • unsigned – 一个布尔值,可选的。

  • zerofill – 可选的。 如果为真,则值将作为左填充零的字符串存储。 请注意,这不影响底层数据库 API 返回的值,后者仍然是数字。

class sqlalchemy.dialects.mysql.ENUM

MySQL ENUM 类型。

成员

init()

类签名

sqlalchemy.dialects.mysql.ENUM (sqlalchemy.types.NativeForEmulated, sqlalchemy.types.Enum, sqlalchemy.dialects.mysql.types._StringType)

method __init__(*enums, **kw)

构造一个 ENUM。

例如:

Column('myenum', ENUM("foo", "bar", "baz"))

参数:

  • enums

    此 ENUM 的有效值范围。 在枚举中的值不带引号,生成模式时将被转义并用单引号括起来。 此对象还可以是符合 PEP-435 的枚举类型。

  • strict

    此标志不起作用。

    版本中更改:MySQL ENUM 类型以及基本 Enum 类型现在验证所有 Python 数据值。

  • charset – 可选的,用于此字符串值的列级字符集。 优先于 ‘ascii’ 或 ‘unicode’ 简写。

  • collation – 可选的,用于此字符串值的列级排序。 优先于 ‘binary’ 简写。

  • ascii – 默认为 False:latin1 字符集的简写,生成模式中的 ASCII。

  • unicode – 默认为 False:ucs2 字符集的简写,生成模式中的 UNICODE。

  • binary – 默认为 False:简写,选择与列的字符集匹配的二进制排序类型。 在模式中生成 BINARY。 这不影响存储的数据类型,只影响字符数据的排序。

class sqlalchemy.dialects.mysql.FLOAT

MySQL FLOAT 类型。

成员

init()

类签名

sqlalchemy.dialects.mysql.FLOATsqlalchemy.dialects.mysql.types._FloatTypesqlalchemy.types.FLOAT)。

method __init__(precision=None, scale=None, asdecimal=False, **kw)

构造一个 FLOAT。

参数:

  • precision – 此数字中的总位数。如果 scale 和 precision 都为 None,则将值存储到服务器允许的限制。

  • scale – 小数点后的数字位数。

  • unsigned – 一个布尔值,可选。

  • zerofill – 可选。如果为 true,则值将作为左填充零的字符串存储。请注意,这不会影响底层数据库 API 返回的值,这些值仍然是数值型的。

class sqlalchemy.dialects.mysql.INTEGER

MySQL 的 INTEGER 类型。

成员

init()

类签名

sqlalchemy.dialects.mysql.INTEGERsqlalchemy.dialects.mysql.types._IntegerTypesqlalchemy.types.INTEGER)。

method __init__(display_width=None, **kw)

构造一个 INTEGER。

参数:

  • display_width – 可选,此数字的最大显示宽度。

  • unsigned – 一个布尔值,可选。

  • zerofill – 可选。如果为 true,则值将作为左填充零的字符串存储。请注意,这不会影响底层数据库 API 返回的值,这些值仍然是数值型的。

class sqlalchemy.dialects.mysql.JSON

MySQL 的 JSON 类型。

从 5.7 版本开始,MySQL 支持 JSON。从 10.2 版本开始,MariaDB 支持 JSON(作为 LONGTEXT 的别名)。

JSON在针对 MySQL 或 MariaDB 后端使用基本的JSON数据类型时会自动使用。

另请参阅

JSON - 通用跨平台 JSON 数据类型的主要文档。

JSON类型支持将 JSON 值持久化,以及通过适应操作在数据库级别呈现JSON_EXTRACT函数所提供的核心索引操作。

类签名

sqlalchemy.dialects.mysql.JSONsqlalchemy.types.JSON)。

class sqlalchemy.dialects.mysql.LONGBLOB

MySQL 的 LONGBLOB 类型,用于二进制数据长达 2³² 字节。

类签名

sqlalchemy.dialects.mysql.LONGBLOBsqlalchemy.types._Binary)。

class sqlalchemy.dialects.mysql.LONGTEXT

MySQL 的 LONGTEXT 类型,用于存储编码长达 2³² 字节的字符。

成员

init()

类签名

class sqlalchemy.dialects.mysql.LONGTEXT (sqlalchemy.dialects.mysql.types._StringType)

method __init__(**kwargs)

构建一个 LONGTEXT。

参数:

  • charset – 可选,该字符串值的列级字符集。优先于 'ascii' 或 'unicode' 简写。

  • collation – 可选,该字符串值的列级排序规则。优先于 'binary' 简写。

  • ascii – 默认为 False:latin1 字符集的简写,在模式中生成 ASCII。

  • unicode – 默认为 False:ucs2 字符集的简写,在模式中生成 UNICODE。

  • national – 可选。如果为真,则使用服务器配置的国家字符集。

  • binary – 默认为 False:简写,选择与列字符集匹配的二进制排序规则类型。在模式中生成 BINARY。这不影响存储的数据类型,只影响字符数据的排序规则。

class sqlalchemy.dialects.mysql.MEDIUMBLOB

MySQL MEDIUMBLOB 类型,用于最多 2²⁴ 字节的二进制数据。

类签名

class sqlalchemy.dialects.mysql.MEDIUMBLOB (sqlalchemy.types._Binary)

class sqlalchemy.dialects.mysql.MEDIUMINT

MySQL MEDIUMINTEGER 类型。

成员

init()

类签名

class sqlalchemy.dialects.mysql.MEDIUMINT (sqlalchemy.dialects.mysql.types._IntegerType)

method __init__(display_width=None, **kw)

构建一个 MEDIUMINTEGER

参数:

  • display_width – 可选,此数字的最大显示宽度。

  • unsigned – 一个布尔值,可选。

  • zerofill – 可选。如果为真,则将值存储为左填充的带零字符串。请注意,这不会影响底层数据库 API 返回的值,这些值仍然是数字。

class sqlalchemy.dialects.mysql.MEDIUMTEXT

MySQL MEDIUMTEXT 类型,用于最多编码 2²⁴ 字节的字符存储。

成员

init()

类签名

class sqlalchemy.dialects.mysql.MEDIUMTEXT (sqlalchemy.dialects.mysql.types._StringType)

method __init__(**kwargs)

构建一个 MEDIUMTEXT。

参数:

  • charset – 可选,该字符串值的列级字符集。优先于 'ascii' 或 'unicode' 简写。

  • collation – 可选,该字符串值的列级排序规则。优先于 'binary' 简写。

  • ascii – 默认为 False:latin1 字符集的简写,在模式中生成 ASCII。

  • unicode – 默认为 False:ucs2 字符集的简写,在模式中生成 UNICODE。

  • national – 可选。如果为真,则使用服务器配置的国家字符集。

  • binary – 默认为 False:简写,选择与列字符集匹配的二进制排序规则类型。在模式中生成 BINARY。这不影响存储的数据类型,只影响字符数据的排序规则。

class sqlalchemy.dialects.mysql.NCHAR

MySQL NCHAR 类型。

对于服务器配置的国家字符集中的固定长度字符数据。

成员

init()

类签名

sqlalchemy.dialects.mysql.NCHAR (sqlalchemy.dialects.mysql.types._StringType, sqlalchemy.types.NCHAR)

method __init__(length=None, **kwargs)

构造一个 NCHAR。

参数:

  • length – 最大数据长度,以字符为单位。

  • binary – 可选的,使用默认的二进制排序规则进行国家字符集。这不影响存储的数据类型,对于二进制数据,请使用 BINARY 类型。

  • collation – 可选的,请求特定的排序规则。必须与国家字符集兼容。

class sqlalchemy.dialects.mysql.NUMERIC

MySQL NUMERIC 类型。

成员

init()

类签名

sqlalchemy.dialects.mysql.NUMERIC (sqlalchemy.dialects.mysql.types._NumericType, sqlalchemy.types.NUMERIC)

method __init__(precision=None, scale=None, asdecimal=True, **kw)

构造一个 NUMERIC。

参数:

  • precision – 此数字中的总位数。如果比例和精度都为 None,则将值存储到服务器允许的限制。

  • scale – 小数点后的位数。

  • unsigned – 一个布尔值,可选的。

  • zerofill – 可选的。如果为真,则值将存储为左边用零填充的字符串。请注意,这不会影响底层数据库 API 返回的值,该值仍为数字。

class sqlalchemy.dialects.mysql.NVARCHAR

MySQL NVARCHAR 类型。

对于服务器配置的国家字符集中的可变长度字符数据。

成员

init()

类签名

sqlalchemy.dialects.mysql.NVARCHAR (sqlalchemy.dialects.mysql.types._StringType, sqlalchemy.types.NVARCHAR)

method __init__(length=None, **kwargs)

构造一个 NVARCHAR。

参数:

  • length – 最大数据长度,以字符为单位。

  • binary – 可选的,使用默认的二进制排序规则进行国家字符集。这不影响存储的数据类型,对于二进制数据,请使用 BINARY 类型。

  • collation – 可选的,请求特定的排序规则。必须与国家字符集兼容。

class sqlalchemy.dialects.mysql.REAL

MySQL REAL 类型。

成员

init()

类签名

sqlalchemy.dialects.mysql.REAL (sqlalchemy.dialects.mysql.types._FloatType, sqlalchemy.types.REAL)

method __init__(precision=None, scale=None, asdecimal=True, **kw)

构造一个 REAL。

注意

默认情况下,REAL类型从浮点数转换为 Decimal,使用默认为 10 位的截断。指定scale=ndecimal_return_scale=n以更改此比例,或者asdecimal=False以直接将值返回为 Python 浮点数。

参数:

  • precision – 此数字中的总位数。如果比例和精度都为 None,则值将存储到服务器允许的限制。

  • scale – 小数点后的位数。

  • unsigned – 一个布尔值,可选。

  • zerofill – 可选。如果为 true,则值将存储为左侧填充零的字符串。请注意,这不会影响底层数据库 API 返回的值,它们仍然是数值。

class sqlalchemy.dialects.mysql.SET

MySQL SET 类型。

成员

init()

类签名

sqlalchemy.dialects.mysql.SET (sqlalchemy.dialects.mysql.types._StringType)

method __init__(*values, **kw)

构造一个 SET。

例如:

Column('myset', SET("foo", "bar", "baz"))

在这种情况下,潜在值的列表是必需的,因为此集合将用于为表生成 DDL,或者如果设置了SET.retrieve_as_bitwise标志。

参数:

  • values – 此 SET 的有效值范围。这些值不带引号,生成模式时将被转义并用单引号括起。

  • convert_unicode – 与String.convert_unicode相同的标志。

  • collation – 与String.collation相同

  • charset – 与VARCHAR.charset相同。

  • ascii – 与VARCHAR.ascii相同。

  • unicode – 与VARCHAR.unicode相同。

  • binary – 与VARCHAR.binary相同。

  • retrieve_as_bitwise

    如果为 True,则集合类型的数据将使用整数值持久化和选择,其中集合被强制转换为持久化的位掩码。MySQL 允许这种模式,它的优势在于能够明确存储值,例如空字符串''。数据类型将在 SELECT 语句中显示为表达式col + 0,以便将值强制转换为结果集中的整数值。如果希望持久化可以存储空字符串''作为值的集合,则需要此标志。

    警告

    在使用SET.retrieve_as_bitwise时,必须确保集合值的列表与 MySQL 数据库中的完全相同的顺序

class sqlalchemy.dialects.mysql.SMALLINT

MySQL SMALLINTEGER 类型。

成员

init()的初始化方法。

类签名

sqlalchemy.dialects.mysql.SMALLINTsqlalchemy.dialects.mysql.types._IntegerTypesqlalchemy.types.SMALLINT

method __init__(display_width=None, **kw)

构造一个 SMALLINTEGER。

参数:

  • display_width – 可选,此数字的最大显示宽度。

  • unsigned – 一个布尔值,可选。

  • zerofill – 可选。如果为 true,值将以左侧填充零的字符串形式存储。请注意,这不会影响底层数据库 API 返回的值,其仍然是数值。

class sqlalchemy.dialects.mysql.TEXT

MySQL TEXT 类型,用于编码最多 2¹⁶ 字节的字符存储。

类签名

sqlalchemy.dialects.mysql.TEXTsqlalchemy.dialects.mysql.types._StringTypesqlalchemy.types.TEXT

method __init__(length=None, **kw)

构造一个 TEXT。

参数:

  • length – 可选,如果提供,服务器可以通过替换足以存储length字节字符的最小 TEXT 类型来优化存储。

  • charset – 可选,用于此字符串值的列级字符集。优先于‘ascii’或‘unicode’简写。

  • collation – 可选,用于此字符串值的列级排序。优先于‘binary’简写。

  • ascii – 默认为 False:latin1字符集的简写,生成模式中的 ASCII。

  • unicode – 默认为 False:ucs2字符集的简写,生成模式中的 UNICODE。

  • national – 可选。如果为 true,则使用服务器配置的国家字符集。

  • binary – 默认为 False:简写,选择与列的字符集匹配的二进制排序类型。在模式中生成 BINARY。这不会影响存储的数据类型,只会影响字符数据的排序。

class sqlalchemy.dialects.mysql.TIME

MySQL TIME 类型。

成员

init()的初始化方法。

类签名

sqlalchemy.dialects.mysql.TIMEsqlalchemy.types.TIME

method __init__(timezone=False, fsp=None)

构造一个 MySQL TIME 类型。

参数:

  • timezone – MySQL 方言不使用。

  • fsp

    分数秒精度值。MySQL 5.6 支持存储分数秒;在为 TIME 类型发出 DDL 时将使用此参数。

    注意

    DBAPI 驱动程序对分数秒的支持可能有限;当前支持包括 MySQL Connector/Python。

class sqlalchemy.dialects.mysql.TIMESTAMP

MySQL TIMESTAMP 类型。

成员

init()的初始化方法。

类签名

sqlalchemy.dialects.mysql.TIMESTAMP (sqlalchemy.types.TIMESTAMP)

method __init__(timezone=False, fsp=None)

构造一个 MySQL TIMESTAMP 类型。

参数:

  • timezone – MySQL 方言不使用。

  • fsp

    小数秒精度值。MySQL 5.6.4 支持存储小数秒;在为 TIMESTAMP 类型发出 DDL 时将使用此参数。

    注意

    DBAPI 驱动程序对小数秒的支持可能有限;当前支持包括 MySQL Connector/Python。

class sqlalchemy.dialects.mysql.TINYBLOB

MySQL TINYBLOB 类型,用于最多 2⁸ 字节的二进制数据。

类签名

sqlalchemy.dialects.mysql.TINYBLOB (sqlalchemy.types._Binary)

class sqlalchemy.dialects.mysql.TINYINT

MySQL TINYINT 类型。

成员

init()

类签名

sqlalchemy.dialects.mysql.TINYINT (sqlalchemy.dialects.mysql.types._IntegerType)

method __init__(display_width=None, **kw)

构造一个 TINYINT。

参数:

  • display_width – 可选,此数字的最大显示宽度。

  • unsigned – 一个布尔值,可选。

  • zerofill – 可选。如果为 true,则值将作为左填充零的字符串存储。请注意,这不影响底层数据库 API 返回的值,其仍然是数值。

class sqlalchemy.dialects.mysql.TINYTEXT

MySQL TINYTEXT 类型,用于编码最多 2⁸ 字节的字符存储。

成员

init()

类签名

sqlalchemy.dialects.mysql.TINYTEXT (sqlalchemy.dialects.mysql.types._StringType)

method __init__(**kwargs)

构造一个 TINYTEXT。

参数:

  • charset – 可选,此字符串值的列级字符集。优先于 ‘ascii’ 或 ‘unicode’ 简写。

  • collation – 可选,此字符串值的列级排序规则。优先于 ‘binary’ 简写。

  • ascii – 默认为 False:latin1 字符集的简写,在模式中生成 ASCII。

  • unicode – 默认为 False:ucs2 字符集的简写,在模式中生成 UNICODE。

  • national – 可选。如果为 true,则使用服务器配置的国家字符集。

  • binary – 默认为 False:简写,选择与列的字符集匹配的二进制排序类型。在模式中生成 BINARY。这不影响存储的数据类型,只影响字符数据的排序。

class sqlalchemy.dialects.mysql.VARBINARY

SQL VARBINARY 类型。

类签名

sqlalchemy.dialects.mysql.VARBINARY (sqlalchemy.types._Binary)

class sqlalchemy.dialects.mysql.VARCHAR

MySQL VARCHAR 类型,用于可变长度字符数据。

成员

init()

类签名

sqlalchemy.dialects.mysql.VARCHARsqlalchemy.dialects.mysql.types._StringTypesqlalchemy.types.VARCHAR

method __init__(length=None, **kwargs)

构造一个 VARCHAR。

参数:

  • charset – 可选,用于此字符串值的列级字符集。优先于‘ascii’或‘unicode’简写。

  • collation – 可选,用于此字符串值的列级排序。优先于‘binary’简写。

  • ascii – 默认为 False:latin1字符集的简写,模式中生成 ASCII。

  • unicode – 默认为 False:ucs2字符集的简写,模式中生成 UNICODE。

  • national – 可选。如果为 true,则使用服务器配置的国家字符集。

  • binary – 默认为 False:简写,选择与列的字符集匹配的二进制排序类型。在模式中生成 BINARY。这不影响存储的数据类型,只影响字符数据的排序。

class sqlalchemy.dialects.mysql.YEAR

MySQL YEAR 类型,用于存储 1901-2155 年的单字节。

类签名

sqlalchemy.dialects.mysql.YEARsqlalchemy.types.TypeEngine

MySQL DML 构造

对象名称 描述
insert(table) 构造一个 MySQL/MariaDB 特定变体Insert构造。
Insert MySQL 特定的 INSERT 实现。
function sqlalchemy.dialects.mysql.insert(table: _DMLTableArgument) → Insert

构造一个 MySQL/MariaDB 特定变体Insert构造。

sqlalchemy.dialects.mysql.insert()函数创建一个sqlalchemy.dialects.mysql.Insert。这个类基于方言不可知的Insert构造,可以使用 SQLAlchemy Core 中的insert()函数构造。

Insert构造包括额外的方法Insert.on_duplicate_key_update()

class sqlalchemy.dialects.mysql.Insert

MySQL 特定的 INSERT 实现。

添加了针对 MySQL 特定语法的方法,例如 ON DUPLICATE KEY UPDATE。

使用 sqlalchemy.dialects.mysql.insert() 函数创建 Insert 对象。

版本 1.2 中的新功能。

成员

inherit_cache, inserted, on_duplicate_key_update()

类签名

class sqlalchemy.dialects.mysql.Insert (sqlalchemy.sql.expression.Insert)

attribute inherit_cache: bool | None = False

指示此 HasCacheKey 实例是否应使用其直接超类使用的缓存密钥生成方案。

该属性默认为 None,表示构造尚未考虑是否适合参与缓存; 这在功能上等同于将值设置为 False,除了还会发出警告。

如果与对象对应的 SQL 不基于此类的本地属性而更改,并且不基于其超类,则可以在特定类上将此标志设置为 True

另请参见

为自定义构造启用缓存支持 - 设置第三方或用户定义的 SQL 构造的 HasCacheKey.inherit_cache 属性的一般指南。

attribute inserted

为 ON DUPLICATE KEY UPDATE 语句提供“inserted”命名空间

MySQL 的 ON DUPLICATE KEY UPDATE 子句允许引用将要插入的行,通过一个称为 VALUES() 的特殊函数。 此属性提供了此行中的所有列,以便它们可在 ON DUPLICATE KEY UPDATE 子句中的 VALUES() 函数内部引用。 属性被命名为 .inserted,以避免与现有的 Insert.values() 方法冲突。

提示

Insert.inserted 属性是 ColumnCollection 的实例,其提供了与 访问表和列中描述的 Table.c 集合相同的接口。使用此集合,普通名称可以像属性一样访问(例如 stmt.inserted.some_column),但特殊名称和字典方法名称应使用索引访问,例如 stmt.inserted["column name"]stmt.inserted["values"]。有关更多示例,请参阅 ColumnCollection 的文档字符串。

请参阅

插入…在重复键更新(Upsert)时 - 使用 Insert.inserted 的示例

method on_duplicate_key_update(*args: Mapping[Any, Any] | List[Tuple[str, Any]] | ColumnCollection[Any, Any], **kw: Any) → Self

指定 ON DUPLICATE KEY UPDATE 子句。

参数:

**kw – 与 UPDATE 值关联的列键。这些值可以是任何 SQL 表达式或支持的字面 Python 值。

警告

此字典考虑 Python 指定的默认 UPDATE 值或生成函数,例如使用 Column.onupdate 指定的值。这些值不会被用于 ON DUPLICATE KEY UPDATE 类型的 UPDATE,除非在此手动指定值。

参数:

*args

作为传递键/值参数的替代方案,可以将字典或 2 元组列表作为单个位置参数传递。

传递单个字典等效于关键字参数形式:

insert().on_duplicate_key_update({"name": "some name"})

传递 2 元组列表表示 UPDATE 子句中的参数分配应按发送的顺序排序,类似于参数有序更新中总体描述的 Update 构造:

insert().on_duplicate_key_update(
    [("name", "some name"), ("value", "some value")])

版本 1.3 中的更改:参数可以指定为字典或 2 元组列表;后一种形式提供了参数排序。

版本 1.2 中的新功能。

请参阅

插入…在重复键更新(Upsert)时

mysqlclient(MySQL-Python 的分支)

通过 mysqlclient(MySQL-Python 的维护分支)驱动程序支持 MySQL / MariaDB 数据库。

DBAPI

mysqlclient(MySQL-Python 的维护分支)的文档和下载信息(如果适用)可在此处找到:pypi.org/project/mysqlclient/

连接

连接字符串:

mysql+mysqldb://<user>:<password>@<host>[:<port>]/<dbname>

驱动程序状态

mysqlclient DBAPI 是不再维护的 MySQL-Python DBAPI 的一个维护的分支。mysqlclient 支持 Python 2 和 Python 3,并且非常稳定。

Unicode

有关当前关于 Unicode 处理的建议,请参阅 Unicode。### SSL 连接

mysqlclient 和 PyMySQL DBAPIs 接受一个额外的字典,其键为“ssl”,可以使用 create_engine.connect_args 字典来指定:

engine = create_engine(
    "mysql+mysqldb://scott:tiger@192.168.0.134/test",
    connect_args={
        "ssl": {
            "ca": "/home/gord/client-ssl/ca.pem",
            "cert": "/home/gord/client-ssl/client-cert.pem",
            "key": "/home/gord/client-ssl/client-key.pem"
        }
    }
)

为了方便起见,以下键也可以内联指定在 URL 中,它们将被自动解释为“ssl”字典: “ssl_ca”、“ssl_cert”、“ssl_key”、“ssl_capath”、“ssl_cipher”、“ssl_check_hostname”。示例如下:

connection_uri = (
    "mysql+mysqldb://scott:tiger@192.168.0.134/test"
    "?ssl_ca=/home/gord/client-ssl/ca.pem"
    "&ssl_cert=/home/gord/client-ssl/client-cert.pem"
    "&ssl_key=/home/gord/client-ssl/client-key.pem"
)

另请参阅

在 PyMySQL 方言中的 SSL 连接

使用 MySQLdb 与 Google Cloud SQL

Google Cloud SQL 现在建议使用 MySQLdb 方言。使用如下 URL 进行连接:

mysql+mysqldb://root@/<dbname>?unix_socket=/cloudsql/<projectid>:<instancename>

服务器端游标

mysqldb 方言支持服务器端游标。请参阅 服务器端游标。## PyMySQL

通过 PyMySQL 驱动程序支持 MySQL / MariaDB 数据库。

DBAPI

PyMySQL 的文档和下载信息(如果适用)可在以下链接获取:pymysql.readthedocs.io/

连接

连接字符串:

mysql+pymysql://<username>:<password>@<host>/<dbname>[?<options>]

Unicode

有关当前关于 Unicode 处理的建议,请参阅 Unicode。

SSL 连接

PyMySQL DBAPI 接受与 MySQLdb 相同的 SSL 参数,描述如 SSL 连接。请参阅该部分以获取其他示例。

如果服务器使用自动生成的自签名证书或与主机名不匹配(从客户端看),则在 PyMySQL 中也可能需要指示 ssl_check_hostname=false

connection_uri = (
    "mysql+pymysql://scott:tiger@192.168.0.134/test"
    "?ssl_ca=/home/gord/client-ssl/ca.pem"
    "&ssl_cert=/home/gord/client-ssl/client-cert.pem"
    "&ssl_key=/home/gord/client-ssl/client-key.pem"
    "&ssl_check_hostname=false"
)

MySQL-Python 兼容性

pymysql DBAPI 是 MySQL-python(MySQLdb)驱动程序的纯 Python 移植版本,目标是 100% 的兼容性。大多数针对 MySQL-python 的行为说明也适用于 pymysql 驱动程序。## MariaDB-Connector

通过 MariaDB Connector/Python 驱动程序支持 MySQL / MariaDB 数据库。

DBAPI

MariaDB Connector/Python 的文档和下载信息(如果适用)可在以下链接获取:pypi.org/project/mariadb/

连接

连接字符串:

mariadb+mariadbconnector://<user>:<password>@<host>[:<port>]/<dbname>

驱动程序状态

MariaDB Connector/Python 允许 Python 程序使用与 Python DB API 2.0(PEP-249)兼容的 API 访问 MariaDB 和 MySQL 数据库。它是用 C 编写的,使用 MariaDB Connector/C 客户端库进行客户端服务器通信。

注意 mariadb:// 连接 URI 的默认驱动程序仍然是 mysqldb。要使用此驱动程序,需要使用 mariadb+mariadbconnector://。## MySQL-Connector

通过 MySQL Connector/Python 驱动程序支持 MySQL / MariaDB 数据库。

DBAPI

文档和 MySQL Connector/Python 的下载信息(如果适用)可在此处获取:pypi.org/project/mysql-connector-python/

连接

连接字符串:

mysql+mysqlconnector://<user>:<password>@<host>[:<port>]/<dbname>

注意

自 MySQL Connector/Python 发布以来,DBAPI 存在许多问题,其中一些可能仍未解决,并且 mysqlconnector 方言 未经过 SQLAlchemy 的持续集成测试。推荐的 MySQL 方言是 mysqlclient 和 PyMySQL。 ## asyncmy

通过 asyncmy 驱动程序支持 MySQL / MariaDB 数据库。

DBAPI

文档和 asyncmy 的下载信息(如果适用)可在此处获取:github.com/long2ice/asyncmy

连接

连接字符串:

mysql+asyncmy://user:password@host:port/dbname[?key=value&key=value...]

使用特殊的 asyncio 中介层,asyncmy 方言可用作 SQLAlchemy asyncio 扩展包的后端。

此方言通常仅应与create_async_engine()引擎创建函数一起使用:

from sqlalchemy.ext.asyncio import create_async_engine
engine = create_async_engine("mysql+asyncmy://user:pass@hostname/dbname?charset=utf8mb4")
```  ## aiomysql

通过 aiomysql 驱动程序支持 MySQL / MariaDB 数据库。

### DBAPI

文档和 aiomysql 的下载信息(如果适用)可在此处获取:[`github.com/aio-libs/aiomysql`](https://github.com/aio-libs/aiomysql)

### 连接

连接字符串:

```py
mysql+aiomysql://user:password@host:port/dbname[?key=value&key=value...]

aiomysql 方言是 SQLAlchemy 的第二个 Python asyncio 方言。

使用特殊的 asyncio 中介层,aiomysql 方言可用作 SQLAlchemy asyncio 扩展包的后端。

此方言通常仅应与create_async_engine()引擎创建函数一起使用:

from sqlalchemy.ext.asyncio import create_async_engine
engine = create_async_engine("mysql+aiomysql://user:pass@hostname/dbname?charset=utf8mb4")
```  ## cymysql

通过 CyMySQL 驱动程序支持 MySQL / MariaDB 数据库。

### DBAPI

文档和 CyMySQL 的下载信息(如果适用)可在此处获取:[`github.com/nakagami/CyMySQL`](https://github.com/nakagami/CyMySQL)

### 连接

连接字符串:

```py
mysql+cymysql://<username>:<password>@<host>/<dbname>[?<options>]

注意

CyMySQL 方言 未经过 SQLAlchemy 的持续集成测试,可能存在未解决的问题。推荐的 MySQL 方言是 mysqlclient 和 PyMySQL。 ## pyodbc

通过 PyODBC 驱动程序支持 MySQL / MariaDB 数据库。

DBAPI

文档和 PyODBC 的下载信息(如果适用)可在此处获取:pypi.org/project/pyodbc/

连接

连接字符串:

mysql+pyodbc://<username>:<password>@<dsnname>

注意

MySQL 的 PyODBC 方言未经过 SQLAlchemy 的持续集成测试。推荐使用的 MySQL 方言是 mysqlclient 和 PyMySQL。但是,如果您想使用 mysql+pyodbc 方言并需要完全支持utf8mb4字符(包括表情符号等辅助字符),请确保使用当前版本的 MySQL Connector/ODBC 并在 DSN 或连接字符串中指定“ANSI”(不是“Unicode”)驱动程序版本。

通过精确的 pyodbc 连接字符串传递:

import urllib
connection_string = (
    'DRIVER=MySQL ODBC 8.0 ANSI Driver;'
    'SERVER=localhost;'
    'PORT=3307;'
    'DATABASE=mydb;'
    'UID=root;'
    'PWD=(whatever);'
    'charset=utf8mb4;'
)
params = urllib.parse.quote_plus(connection_string)
connection_uri = "mysql+pyodbc:///?odbc_connect=%s" % params

支持 MySQL / MariaDB 数据库。

以下表总结了数据库发布版本的当前支持水平。

支持的 MySQL / MariaDB 版本

支持类型 版本
持续集成完全测试 5.6, 5.7, 8.0 / 10.8, 10.9
正常支持 5.6+ / 10+
尽力而为 5.0.2+ / 5.0.2+

DBAPI 支持

提供以下方言/DBAPI 选项。有关连接信息,请参考各个 DBAPI 部分。

  • mysqlclient(MySQL-Python 的维护分支)

  • PyMySQL

  • MariaDB Connector/Python

  • MySQL Connector/Python

  • asyncmy

  • aiomysql

  • CyMySQL

  • PyODBC

支持的版本和功能

SQLAlchemy 支持从版本 5.0.2 开始的 MySQL,直至现代版本,以及所有现代版本的 MariaDB。有关任何给定服务器版本支持的功能的详细信息,请参阅官方 MySQL 文档。

从版本 1.4 开始更改:支持的最低 MySQL 版本现在是 5.0.2。

MariaDB 支持

MariaDB 变种的 MySQL 保留了与 MySQL 协议的基本兼容性,但这两个产品的发展仍在分歧。在 SQLAlchemy 领域,这两个数据库有一些语法和行为上的差异,SQLAlchemy 会自动适应。要连接到 MariaDB 数据库,不需要对数据库 URL 进行任何更改:

engine = create_engine("mysql+pymysql://user:pass@some_mariadb/dbname?charset=utf8mb4")

在首次连接时,SQLAlchemy 方言采用服务器版本检测方案,确定后端数据库是否报告为 MariaDB。根据此标志,方言可以在必须有不同行为的领域做出不同选择。

仅限 MariaDB 模式

该方言还支持可选的“仅限 MariaDB”连接模式,这对于应用程序使用 MariaDB 特定功能且与 MySQL 数据库不兼容的情况可能很有用。要使用此操作模式,请将上述 URL 中的“mysql”标记替换为“mariadb”:

engine = create_engine("mariadb+pymysql://user:pass@some_mariadb/dbname?charset=utf8mb4")

在第一次连接时,上述引擎会在服务器版本检测检测到后端数据库不是 MariaDB 时引发错误。

当使用以 "mariadb" 为方言名称的引擎时,所有包含名称 “mysql” 的 MySQL 特定选项现在都以 "mariadb" 命名。这意味着像 mysql_engine 这样的选项应该命名为 mariadb_engine,等等。对于同时使用“mysql”和"mariadb"方言 URL 的应用程序,可以同时使用“mysql”和"mariadb"选项:

my_table = Table(
    "mytable",
    metadata,
    Column("id", Integer, primary_key=True),
    Column("textdata", String(50)),
    mariadb_engine="InnoDB",
    mysql_engine="InnoDB",
)

Index(
    "textdata_ix",
    my_table.c.textdata,
    mysql_prefix="FULLTEXT",
    mariadb_prefix="FULLTEXT",
)

当上述结构反映时,会出现类似的行为,即当数据库 URL 基于“mariadb”名称时,选项名称中将包含“mariadb”前缀。

新版本中新增了“mariadb”方言名称,支持 MySQL 方言的“仅 MariaDB 模式”。

MariaDB 支持

MySQL 的 MariaDB 变体保留了与 MySQL 协议的基本兼容性,但这两个产品的开发仍在分歧。在 SQLAlchemy 的领域内,这两个数据库有一些语法和行为上的小差异,SQLAlchemy 会自动适应。连接到 MariaDB 数据库时,不需要对数据库 URL 进行任何更改:

engine = create_engine("mysql+pymysql://user:pass@some_mariadb/dbname?charset=utf8mb4")

在第一次连接时,SQLAlchemy 方言采用了一种服务器版本检测方案,以确定后端数据库是否报告为 MariaDB。根据此标志,方言可以在必须具有不同行为的领域中做出不同选择。

仅 MariaDB 模式

该方言还支持一种 可选的 “仅 MariaDB” 连接模式,这在应用程序使用 MariaDB 特定功能且与 MySQL 数据库不兼容的情况下可能很有用。要使用此操作模式,请将上述 URL 中的 “mysql” 令牌替换为 “mariadb”:

engine = create_engine("mariadb+pymysql://user:pass@some_mariadb/dbname?charset=utf8mb4")

在第一次连接时,上述引擎会在服务器版本检测检测到后端数据库不是 MariaDB 时引发错误。

当使用以 "mariadb" 为方言名称的引擎时,所有包含名称 “mysql” 的 MySQL 特定选项现在都以 "mariadb" 命名。这意味着像 mysql_engine 这样的选项应该命名为 mariadb_engine,等等。对于同时使用“mysql”和"mariadb"方言 URL 的应用程序,可以同时使用“mysql”和"mariadb"选项:

my_table = Table(
    "mytable",
    metadata,
    Column("id", Integer, primary_key=True),
    Column("textdata", String(50)),
    mariadb_engine="InnoDB",
    mysql_engine="InnoDB",
)

Index(
    "textdata_ix",
    my_table.c.textdata,
    mysql_prefix="FULLTEXT",
    mariadb_prefix="FULLTEXT",
)

当上述结构反映时,会出现类似的行为,即当数据库 URL 基于“mariadb”名称时,选项名称中将包含“mariadb”前缀。

新版本中新增了“mariadb”方言名称,支持 MySQL 方言的“仅 MariaDB 模式”。

连接超时和断开连接

MySQL / MariaDB 具有自动关闭连接行为,对于空闲一段固定时间的连接,默认为八小时。要避免出现此问题,可以使用create_engine.pool_recycle 选项,该选项确保如果连接在池中存在了固定数量的秒数,则将其丢弃并替换为新连接:

engine = create_engine('mysql+mysqldb://...', pool_recycle=3600)

对于更全面的池化连接断开检测,包括适应服务器重启和网络问题,可以采用预先 ping 的方法。有关当前方法,请参阅处理断开连接。

另请参阅

处理断开连接 - 关于处理超时连接以及数据库重启的几种技术的背景。

包括存储引擎在内的 CREATE TABLE 参数

MySQL 和 MariaDB 的 CREATE TABLE 语法都包含许多特殊选项,包括ENGINECHARSETMAX_ROWSROW_FORMATINSERT_METHOD等等。为了适应这些参数的渲染,需要指定形式mysql_argument_name="value"。例如,要指定一个具有ENGINEInnoDBCHARSETutf8mb4KEY_BLOCK_SIZE1024的表:

Table('mytable', metadata,
      Column('data', String(32)),
      mysql_engine='InnoDB',
      mysql_charset='utf8mb4',
      mysql_key_block_size="1024"
     )

在支持 仅限 MariaDB 模式 时,还必须包含对“mariadb”前缀的类似键。当然,值可以独立变化,以便可以维护 MySQL 与 MariaDB 上的不同设置:

# support both "mysql" and "mariadb-only" engine URLs

Table('mytable', metadata,
      Column('data', String(32)),

      mysql_engine='InnoDB',
      mariadb_engine='InnoDB',

      mysql_charset='utf8mb4',
      mariadb_charset='utf8',

      mysql_key_block_size="1024"
      mariadb_key_block_size="1024"

     )

MySQL / MariaDB 方言通常会将指定为mysql_keyword_name的任何关键字转换为CREATE TABLE语句中的KEYWORD_NAME。其中少数名称将以空格而不是下划线呈现;为支持此功能,MySQL 方言具有对这些特定名称的认知,其中包括DATA DIRECTORY(例如mysql_data_directory)、CHARACTER SET(例如mysql_character_set)和INDEX DIRECTORY(例如mysql_index_directory)。

最常见的参数是mysql_engine,它指的是表格的存储引擎。历史上,MySQL 服务器安装通常默认将此值设置为MyISAM,尽管较新的版本可能默认为InnoDBInnoDB 引擎通常更受欢迎,因为它支持事务和外键。

在 MySQL / MariaDB 数据库中创建的具有MyISAM存储引擎的Table将基本上是非事务性的,这意味着任何涉及此表的 INSERT/UPDATE/DELETE 语句都将被调用为自动提交。它也不支持外键约束;虽然CREATE TABLE语句接受外键选项,但在使用MyISAM存储引擎时,这些参数将被丢弃。反映这样一张表也不会产生外键约束信息。

为了完全原子性的事务以及支持外键约束,所有参与的CREATE TABLE语句必须指定一个事务性引擎,在绝大多数情况下是InnoDB

大小写敏感和表反射

MySQL 和 MariaDB 都不一致地支持区分大小写的标识符名称,其支持基于底层操作系统的具体细节。然而,已经观察到,无论存在何种大小写敏感性行为,外键声明中的表名 始终 以全部小写的形式从数据库接收到,这使得无法准确反映使用混合大小写标识符名称的相互关联表的模式。

因此,强烈建议在 SQLAlchemy 中以及在 MySQL / MariaDB 数据库本身中将表名声明为全部小写,特别是如果要使用数据库反射功能的话。

事务隔离级别

所有 MySQL / MariaDB 方言都支持通过方言特定参数 create_engine.isolation_level(由 create_engine() 接受)以及作为传递给 Connection.execution_options() 的参数的 Connection.execution_options.isolation_level 参数来设置事务隔离级别。此功能通过为每个新连接发出命令 SET SESSION TRANSACTION ISOLATION LEVEL <level> 来工作。对于特殊的 AUTOCOMMIT 隔离级别,使用了特定于 DBAPI 的技术。

使用 create_engine() 设置隔离级别:

engine = create_engine(
                "mysql+mysqldb://scott:tiger@localhost/test",
                isolation_level="READ UNCOMMITTED"
            )

使用每个连接的执行选项进行设置:

connection = engine.connect()
connection = connection.execution_options(
    isolation_level="READ COMMITTED"
)

isolation_level的有效值包括:

  • READ COMMITTED

  • READ UNCOMMITTED

  • REPEATABLE READ

  • SERIALIZABLE

  • AUTOCOMMIT

特殊值 AUTOCOMMIT 利用了特定 DBAPI 提供的各种“自动提交”属性,目前由 MySQLdb、MySQL-Client、MySQL-Connector Python 和 PyMySQL 支持。使用它,数据库连接将返回 SELECT @@autocommit; 的值为真。

还有更多隔离级别配置选项,例如与主 Engine 关联的“子引擎”对象,每个对象应用不同的隔离级别设置。请参阅 设置事务隔离级别,包括 DBAPI 自动提交 中的讨论。

另请参阅

设置事务隔离级别,包括 DBAPI 自动提交

AUTO_INCREMENT 行为

在创建表时,SQLAlchemy 将自动在第一个未标记为外键的Integer主键列上设置AUTO_INCREMENT

>>> t = Table('mytable', metadata,
...   Column('mytable_id', Integer, primary_key=True)
... )
>>> t.create()
CREATE TABLE mytable (
 id INTEGER NOT NULL AUTO_INCREMENT,
 PRIMARY KEY (id)
)

通过将False传递给Column.autoincrement参数,您可以禁用此行为。此标志还可用于在某些存储引擎中启用多列键中的辅助列的自动增量:

Table('mytable', metadata,
      Column('gid', Integer, primary_key=True, autoincrement=False),
      Column('id', Integer, primary_key=True)
     )

服务器端游标

服务器端游标支持适用于 mysqlclient、PyMySQL、mariadbconnector 方言,也可能适用于其他方言。如果可用,可以使用“buffered=True/False”标志,也可以在内部使用诸如MySQLdb.cursors.SSCursorpymysql.cursors.SSCursor这样的类。

服务器端游标通过使用Connection.execution_options.stream_results连接执行选项基于语句来启用:

with engine.connect() as conn:
    result = conn.execution_options(stream_results=True).execute(text("select * from table"))

请注意,某些类型的 SQL 语句可能不支持服务器端游标;通常,只应该使用返回行的 SQL 语句来使用此选项。

自版本 1.4 弃用:dialect-level server_side_cursors 标志已弃用,并将在未来版本中删除。请使用Connection.stream_results执行选项来支持无缓冲游标。

另请参阅

使用服务器端游标(也称为流式结果)

Unicode

字符集选择

大多数 MySQL / MariaDB DBAPI 都提供了设置连接的客户端字符集的选项。这通常使用 URL 中的charset参数传递,例如:

e = create_engine(
    "mysql+pymysql://scott:tiger@localhost/test?charset=utf8mb4")

此字符集是连接的客户端字符集。某些 MySQL DBAPI 将默认将此设置为诸如latin1之类的值,而某些将使用my.cnf文件中的default-character-set设置。应该查阅正在使用的 DBAPI 的文档以获取特定的行为。

对于 Unicode 使用的编码传统上是'utf8'。然而,对于 MySQL 版本 5.5.3 和 MariaDB 5.5 以后的版本,引入了一个新的 MySQL 特定编码'utf8mb4',而且从 MySQL 8.0 开始,如果在任何服务器端指令中指定了普通的utf8,服务器将发出警告,并替换为utf8mb3。引入这种新编码的原因是因为 MySQL 的传统 utf-8 编码仅支持最多三个字节的码点,而不是四个。因此,在与包含超过三个字节大小的码点的 MySQL 或 MariaDB 数据库通信时,如果数据库和客户端 DBAPI 都支持,优先使用这种新的字符集,如下所示:

e = create_engine(
    "mysql+pymysql://scott:tiger@localhost/test?charset=utf8mb4")

所有现代 DBAPI 应该支持utf8mb4字符集。

要在使用传统utf8创建的模式中使用utf8mb4编码,可能需要对 MySQL/MariaDB 模式和/或服务器配置进行更改。

另请参阅

utf8mb4 字符集 - MySQL 文档中

处理二进制数据警告和 Unicode

MySQL 版本 5.6、5.7 和以后(在本文写作时不包括 MariaDB)现在在尝试将二进制数据传递到数据库时发出警告,而在二进制数据本身不适用于该编码时,也放置了字符集编码:

default.py:509: Warning: (1300, "Invalid utf8mb4 character string:
'F9876A'")
  cursor.execute(statement, parameters)

此警告是因为 MySQL 客户端库试图将二进制字符串解释为 unicode 对象,即使使用了诸如LargeBinary之类的数据类型。为了解决此问题,SQL 语句需要在任何呈现如下的非 NULL 值之前存在一个二进制“字符集引导”:

INSERT INTO table (data) VALUES (_binary %s)

这些字符集引导由 DBAPI 驱动程序提供,假设使用的是 mysqlclient 或 PyMySQL(两者都建议使用)。将查询字符串参数binary_prefix=true添加到 URL 中以修复此警告:

# mysqlclient
engine = create_engine(
    "mysql+mysqldb://scott:tiger@localhost/test?charset=utf8mb4&binary_prefix=true")

# PyMySQL
engine = create_engine(
    "mysql+pymysql://scott:tiger@localhost/test?charset=utf8mb4&binary_prefix=true")

binary_prefix标志可能会或可能不会被其他 MySQL 驱动程序支持。

由于 MySQL 驱动程序直接将参数呈现到 SQL 字符串中,因此无法可靠地呈现此_binary前缀,因为它不会与 NULL 值一起使用,而 NULL 值可以作为绑定参数发送。由于 MySQL 驱动程序直接将参数呈现到 SQL 字符串中,因此这个附加关键字被传递的地方效率最高。

另请参阅

字符集引导 - MySQL 网站上

字符集选择

大多数 MySQL / MariaDB DBAPI 都提供了为连接设置客户端字符集的选项。这通常使用 URL 中的charset参数传递,例如:

e = create_engine(
    "mysql+pymysql://scott:tiger@localhost/test?charset=utf8mb4")

这个字符集是客户端字符集用于连接。某些 MySQL DBAPI 将其默认为诸如latin1之类的值,有些将使用my.cnf文件中的default-character-set设置。应该咨询使用的 DBAPI 的文档以获取具体行为。

用于 Unicode 的编码传统上是'utf8'。然而,对于 MySQL 版本 5.5.3 和 MariaDB 5.5 以及更高版本,引入了一个新的 MySQL 特定编码'utf8mb4',并且从 MySQL 8.0 开始,如果在任何服务器端指令中指定了普通的utf8,服务器将发出警告,并替换为utf8mb3。引入这种新编码的原因是因为 MySQL 的传统 utf-8 编码只支持最多三个字节的代码点,而不是四个。因此,当与包含超过三个字节大小的代码点的 MySQL 或 MariaDB 数据库通信时,如果数据库和客户端 DBAPI 都支持,首选使用这种新的字符集,如下所示:

e = create_engine(
    "mysql+pymysql://scott:tiger@localhost/test?charset=utf8mb4")

所有现代 DBAPI 应该支持utf8mb4字符集。

为了在使用了传统utf8创建的模式中使用utf8mb4编码,可能需要对 MySQL/MariaDB 模式和/或服务器配置进行更改。

另请参阅

utf8mb4 字符集 - 在 MySQL 文档中

处理二进制数据警告和 Unicode

MySQL 版本 5.6、5.7 及更高版本(在撰写本文时不包括 MariaDB)现在在尝试将二进制数据传递给数据库时发出警告,同时还存在字符集编码,当二进制数据本身对该编码无效时:

default.py:509: Warning: (1300, "Invalid utf8mb4 character string:
'F9876A'")
  cursor.execute(statement, parameters)

此警告是由于 MySQL 客户端库试图将二进制字符串解释为 Unicode 对象,即使使用了诸如LargeBinary之类的数据类型。为了解决这个问题,SQL 语句在任何呈现如下的非 NULL 值之前需要存在一个二进制“字符集介绍”:

INSERT INTO table (data) VALUES (_binary %s)

这些字符集介绍由 DBAPI 驱动程序提供,假设使用 mysqlclient 或 PyMySQL(两者都推荐)。在 URL 中添加查询字符串参数binary_prefix=true以修复此警告:

# mysqlclient
engine = create_engine(
    "mysql+mysqldb://scott:tiger@localhost/test?charset=utf8mb4&binary_prefix=true")

# PyMySQL
engine = create_engine(
    "mysql+pymysql://scott:tiger@localhost/test?charset=utf8mb4&binary_prefix=true")

binary_prefix标志可能会或可能不会被其他 MySQL 驱动程序支持。

SQLAlchemy 本身无法可靠地呈现这个_binary前缀,因为它不适用于 NULL 值,而 NULL 值是可以作为绑定参数发送的。由于 MySQL 驱动程序直接将参数呈现到 SQL 字符串中,这是传递此附加关键字的最有效位置。

另请参阅

字符集介绍 - 在 MySQL 网站上

ANSI 引用风格

MySQL / MariaDB 有两种不同的标识符“引号样式”,一种使用反引号,另一种使用引号,例如some_identifier vs. "some_identifier"。 所有 MySQL 方言通过检查在与特定Engine建立连接时的 sql_mode 的值来检测正在使用的版本。 当与特定池的给定 DBAPI 连接首次创建连接时,此引号样式用于渲染表和列名称以及反映现有数据库结构。 检测完全自动,不需要特殊配置来使用任何引号样式。

更改 sql_mode

MySQL 支持在服务器和客户端上运行多种服务器 SQL 模式。 要更改给定应用程序的sql_mode,开发人员可以利用 SQLAlchemy 的事件系统。

在下面的示例中,事件系统用于在first_connectconnect事件上设置sql_mode

from sqlalchemy import create_engine, event

eng = create_engine("mysql+mysqldb://scott:tiger@localhost/test", echo='debug')

# `insert=True` will ensure this is the very first listener to run
@event.listens_for(eng, "connect", insert=True)
def connect(dbapi_connection, connection_record):
    cursor = dbapi_connection.cursor()
    cursor.execute("SET sql_mode = 'STRICT_ALL_TABLES'")

conn = eng.connect()

在上面说明的示例中,“connect”事件将在特定 DBAPI 连接首次为给定的池创建连接时在连接池将连接提供给连接池之前在连接上调用“SET”语句。 此外,因为函数已注册为insert=True,它将被添加到注册函数的内部列表的开头。

MySQL / MariaDB SQL 扩展

许多 MySQL / MariaDB SQL 扩展都通过 SQLAlchemy 的通用函数和操作符支持:

table.select(table.c.password==func.md5('plaintext'))
table.select(table.c.username.op('regexp')('^[a-d]'))

当然,任何有效的 SQL 语句也可以作为字符串执行。

目前有一些有限的直接支持 MySQL / MariaDB SQL 扩展到 SQL 的方法。

  • INSERT..ON DUPLICATE KEY UPDATE:参见 INSERT…ON DUPLICATE KEY UPDATE(Upsert)

  • SELECT pragma,请使用Select.prefix_with()Query.prefix_with()

    select(...).prefix_with(['HIGH_PRIORITY', 'SQL_SMALL_RESULT'])
    
  • 使用 LIMIT 的 UPDATE:

    update(..., mysql_limit=10, mariadb_limit=10)
    
  • 优化器提示,请使用Select.prefix_with()Query.prefix_with()

    select(...).prefix_with("/*+ NO_RANGE_OPTIMIZATION(t4 PRIMARY) */")
    
  • 索引提示,请使用Select.with_hint()Query.with_hint()

    select(...).with_hint(some_table, "USE INDEX xyz")
    
  • MATCH 操作符支持:

    from sqlalchemy.dialects.mysql import match
    select(...).where(match(col1, col2, against="some expr").in_boolean_mode())
    
    .. seealso::
    
        :class:`_mysql.match`
    

INSERT/DELETE…RETURNING

MariaDB 方言支持 10.5+ 的INSERT..RETURNINGDELETE..RETURNING(10.0+)语法。INSERT..RETURNING可能会在某些情况下自动使用,以获取新生成的标识符,而不是使用cursor.lastrowid的传统方法,但是目前在简单的单语句情况下仍然更喜欢使用cursor.lastrowid,因为其性能更好。

要指定显式的RETURNING子句,请在每个语句上使用_UpdateBase.returning()方法:

# INSERT..RETURNING
result = connection.execute(
    table.insert().
    values(name='foo').
    returning(table.c.col1, table.c.col2)
)
print(result.all())

# DELETE..RETURNING
result = connection.execute(
    table.delete().
    where(table.c.name=='foo').
    returning(table.c.col1, table.c.col2)
)
print(result.all())

2.0 版中的新功能:添加了对 MariaDB RETURNING 的支持

INSERT…ON DUPLICATE KEY UPDATE(更新插入)

MySQL / MariaDB 允许通过INSERT语句的ON DUPLICATE KEY UPDATE子句将行“upsert”(更新或插入)到表中。只有候选行与表中现有的主键或唯一键不匹配时,才会插入候选行;否则,将执行更新。该语句允许单独指定要插入的值与要更新的值。

SQLAlchemy 通过 MySQL 特定的insert()函数提供ON DUPLICATE KEY UPDATE支持,该函数提供了生成方法Insert.on_duplicate_key_update()

>>> from sqlalchemy.dialects.mysql import insert

>>> insert_stmt = insert(my_table).values(
...     id='some_existing_id',
...     data='inserted value')

>>> on_duplicate_key_stmt = insert_stmt.on_duplicate_key_update(
...     data=insert_stmt.inserted.data,
...     status='U'
... )
>>> print(on_duplicate_key_stmt)
INSERT  INTO  my_table  (id,  data)  VALUES  (%s,  %s)
ON  DUPLICATE  KEY  UPDATE  data  =  VALUES(data),  status  =  %s 

与 PostgreSQL 的“ON CONFLICT”短语不同,“ON DUPLICATE KEY UPDATE”短语将始终匹配任何主键或唯一键,并且始终在匹配时执行 UPDATE;它没有选项可以引发错误或跳过执行 UPDATE。

ON DUPLICATE KEY UPDATE用于对已经存在的行执行更新,使用新值的任何组合以及建议插入的值。这些值通常使用关键字参数传递给Insert.on_duplicate_key_update()给定列键值(通常是列的名称,除非它指定Column.key)作为键和文字或 SQL 表达式作为值:

>>> insert_stmt = insert(my_table).values(
...          id='some_existing_id',
...          data='inserted value')

>>> on_duplicate_key_stmt = insert_stmt.on_duplicate_key_update(
...     data="some data",
...     updated_at=func.current_timestamp(),
... )

>>> print(on_duplicate_key_stmt)
INSERT  INTO  my_table  (id,  data)  VALUES  (%s,  %s)
ON  DUPLICATE  KEY  UPDATE  data  =  %s,  updated_at  =  CURRENT_TIMESTAMP 

UpdateBase.values()类似,还接受其他参数形式,包括单个字典:

>>> on_duplicate_key_stmt = insert_stmt.on_duplicate_key_update(
...     {"data": "some data", "updated_at": func.current_timestamp()},
... )

以及一个 2 元组的列表,它将自动提供类似于参数顺序更新中描述的方法的参数排序 UPDATE 语句。与Update对象不同,不需要指定特殊标志来指定意图,因为此上下文中的参数形式是清晰明了的:

>>> on_duplicate_key_stmt = insert_stmt.on_duplicate_key_update(
...     [
...         ("data", "some data"),
...         ("updated_at", func.current_timestamp()),
...     ]
... )

>>> print(on_duplicate_key_stmt)
INSERT  INTO  my_table  (id,  data)  VALUES  (%s,  %s)
ON  DUPLICATE  KEY  UPDATE  data  =  %s,  updated_at  =  CURRENT_TIMESTAMP 

在 1.3 版中更改:支持 MySQL ON DUPLICATE KEY UPDATE 中的参数顺序 UPDATE 子句

警告

Insert.on_duplicate_key_update() 方法 考虑 Python 端的默认 UPDATE 值或生成函数,例如,那些使用 Column.onupdate 指定的值。这些值不会在 ON DUPLICATE KEY 样式的 UPDATE 中生效,除非它们在参数中手动指定。

为了引用所提出的插入行,特殊别名 Insert.inserted 可作为 Insert 对象的属性使用;这个对象是一个包含目标表所有列的 ColumnCollection

>>> stmt = insert(my_table).values(
...     id='some_id',
...     data='inserted value',
...     author='jlh')

>>> do_update_stmt = stmt.on_duplicate_key_update(
...     data="updated value",
...     author=stmt.inserted.author
... )

>>> print(do_update_stmt)
INSERT  INTO  my_table  (id,  data,  author)  VALUES  (%s,  %s,  %s)
ON  DUPLICATE  KEY  UPDATE  data  =  %s,  author  =  VALUES(author) 

渲染时,“inserted”命名空间将产生表达式 VALUES(<columnname>)

新版本 1.2 中:增加了对 MySQL ON DUPLICATE KEY UPDATE 子句的支持

行数支持

SQLAlchemy 将 DBAPI cursor.rowcount 属性标准化为“UPDATE 或 DELETE 语句匹配的行数”的常规定义。这与大多数 MySQL DBAPI 驱动程序的默认设置相矛盾,后者是“实际修改/删除的行数”。因此,SQLAlchemy MySQL 方言总是在连接时添加 constants.CLIENT.FOUND_ROWS 标志,或者等效于目标方言的标志。这个设置目前是硬编码的。

另请参见

CursorResult.rowcount

MySQL / MariaDB 特定的索引选项

可用的 MySQL 和 MariaDB 特定的 Index 构造扩展。

索引长度

MySQL 和 MariaDB 都提供了创建具有特定长度的索引条目的选项,这里的“长度”指的是每个值中将成为索引一部分的字符或字节的数量。SQLAlchemy 通过 mysql_length 和/或 mariadb_length 参数提供了这个特性:

Index('my_index', my_table.c.data, mysql_length=10, mariadb_length=10)

Index('a_b_idx', my_table.c.a, my_table.c.b, mysql_length={'a': 4,
                                                           'b': 9})

Index('a_b_idx', my_table.c.a, my_table.c.b, mariadb_length={'a': 4,
                                                           'b': 9})

前缀长度以字符形式给出,用于非二进制字符串类型,以字节形式给出,用于二进制字符串类型。传递给关键字参数的值 必须 是一个整数(因此,为索引的所有列指定相同的前缀长度值),或者是一个字典,其中键是列名,值是相应列的前缀长度值。MySQL 和 MariaDB 仅允许索引列的长度为 CHAR、VARCHAR、TEXT、BINARY、VARBINARY 和 BLOB。

索引前缀

MySQL 存储引擎允许在创建索引时指定索引前缀。SQLAlchemy 通过 Indexmysql_prefix 参数提供了这个功能:

Index('my_index', my_table.c.data, mysql_prefix='FULLTEXT')

传递给关键字参数的值将简单地传递给底层的 CREATE INDEX,因此它 必须 是你的 MySQL 存储引擎的有效索引前缀。

另请参阅

创建索引 - MySQL 文档

索引类型

一些 MySQL 存储引擎允许在创建索引或主键约束时指定索引类型。SQLAlchemy 通过 Indexmysql_using 参数提供了这个功能:

Index('my_index', my_table.c.data, mysql_using='hash', mariadb_using='hash')

以及 PrimaryKeyConstraint 上的 mysql_using 参数:

PrimaryKeyConstraint("data", mysql_using='hash', mariadb_using='hash')

传递给关键字参数的值将简单地传递给底层的 CREATE INDEX 或 PRIMARY KEY 子句,因此它 必须 是你的 MySQL 存储引擎的有效索引类型。

更多信息请参阅:

dev.mysql.com/doc/refman/5.0/en/create-index.html

dev.mysql.com/doc/refman/5.0/en/create-table.html

索引解析器

在 MySQL 中,CREATE FULLTEXT INDEX 还支持 “WITH PARSER” 选项。这可以使用关键字参数 mysql_with_parser 实现:

Index(
    'my_index', my_table.c.data,
    mysql_prefix='FULLTEXT', mysql_with_parser="ngram",
    mariadb_prefix='FULLTEXT', mariadb_with_parser="ngram",
)

版本 1.3 中的新增内容。

索引长度

MySQL 和 MariaDB 都提供了创建带有一定长度的索引条目的选项,其中“长度”指的是将成为索引一部分的每个值中的字符或字节数。SQLAlchemy 通过 mysql_length 和/或 mariadb_length 参数提供了这个功能:

Index('my_index', my_table.c.data, mysql_length=10, mariadb_length=10)

Index('a_b_idx', my_table.c.a, my_table.c.b, mysql_length={'a': 4,
                                                           'b': 9})

Index('a_b_idx', my_table.c.a, my_table.c.b, mariadb_length={'a': 4,
                                                           'b': 9})

非二进制字符串类型的字符给出前缀长度,二进制字符串类型的字节给出前缀长度。传递给关键字参数的值 必须 是整数(因此为所有索引列指定相同的前缀长度值)或字典,其中键是列名,值是对应列的前缀长度值。MySQL 和 MariaDB 只允许索引列的长度为 CHAR、VARCHAR、TEXT、BINARY、VARBINARY 和 BLOB 类型时指定长度。

索引前缀

MySQL 存储引擎允许在创建索引时指定索引前缀。SQLAlchemy 通过 Indexmysql_prefix 参数提供了这个功能:

Index('my_index', my_table.c.data, mysql_prefix='FULLTEXT')

传递给关键字参数的值将简单地传递给底层的 CREATE INDEX,因此它 必须 是你的 MySQL 存储引擎的有效索引前缀。

另请参阅

创建索引 - MySQL 文档

索引类型

一些 MySQL 存储引擎允许您在创建索引或主键约束时指定索引类型。SQLAlchemy 通过Index上的mysql_using参数提供了此功能:

Index('my_index', my_table.c.data, mysql_using='hash', mariadb_using='hash')

以及PrimaryKeyConstraint上的mysql_using参数:

PrimaryKeyConstraint("data", mysql_using='hash', mariadb_using='hash')

传递给关键字参数的值将简单地传递给底层的 CREATE INDEX 或 PRIMARY KEY 子句,因此它必须是您的 MySQL 存储引擎的有效索引类型。

更多信息请参见:

dev.mysql.com/doc/refman/5.0/en/create-index.html

dev.mysql.com/doc/refman/5.0/en/create-table.html

索引解析器

MySQL 中的 CREATE FULLTEXT INDEX 也支持“WITH PARSER”选项。可以使用关键字参数mysql_with_parser

Index(
    'my_index', my_table.c.data,
    mysql_prefix='FULLTEXT', mysql_with_parser="ngram",
    mariadb_prefix='FULLTEXT', mariadb_with_parser="ngram",
)

1.3 版本中的新功能。

MySQL / MariaDB 外键

MySQL 和 MariaDB 在外键方面的行为有一些重要的注意事项。

需要避免的外键参数

MySQL 和 MariaDB 都不支持外键参数“DEFERRABLE”、“INITIALLY”或“MATCH”。在ForeignKeyConstraintForeignKey上使用deferrableinitially关键字参数将导致这些关键字在 DDL 表达式中呈现,然后在 MySQL 或 MariaDB 上引发错误。为了在 MySQL / MariaDB 后端忽略这些关键字的情况下在外键上使用这些关键字,使用自定义编译规则:

from sqlalchemy.ext.compiler import compiles
from sqlalchemy.schema import ForeignKeyConstraint

@compiles(ForeignKeyConstraint, "mysql", "mariadb")
def process(element, compiler, **kw):
    element.deferrable = element.initially = None
    return compiler.visit_foreign_key_constraint(element, **kw)

"MATCH" 关键字实际上更加隐匿,且在与 MySQL 或 MariaDB 后端一起明确禁止。此参数被 MySQL / MariaDB 默默忽略,但此外还导致 ON UPDATE 和 ON DELETE 选项也被后端忽略。因此,永远不应该在 MySQL / MariaDB 后端使用 MATCH;与 DEFERRABLE 和 INITIALLY 一样,可以使用自定义编译规则在 DDL 定义时纠正 ForeignKeyConstraint。

外键约束的反射

并非所有 MySQL / MariaDB 存储引擎都支持外键。当使用非常常见的MyISAM MySQL 存储引擎时,表格反射加载的信息将不包括外键。对于这些表格,您可以在反射时提供ForeignKeyConstraint

Table('mytable', metadata,
      ForeignKeyConstraint(['other_id'], ['othertable.other_id']),
      autoload_with=engine
     )

另请参阅

创建表格参数,包括存储引擎

需要避免的外键参数

MySQL 和 MariaDB 都不支持外键参数“DEFERRABLE”、“INITIALLY”或“MATCH”。在 ForeignKeyConstraintForeignKey 中使用 deferrableinitially 关键字参数会使这些关键字在 DDL 表达式中呈现,然后在 MySQL 或 MariaDB 上引发错误。为了在外键上使用这些关键字,同时忽略 MySQL / MariaDB 后端上的它们,可以使用自定义编译规则:

from sqlalchemy.ext.compiler import compiles
from sqlalchemy.schema import ForeignKeyConstraint

@compiles(ForeignKeyConstraint, "mysql", "mariadb")
def process(element, compiler, **kw):
    element.deferrable = element.initially = None
    return compiler.visit_foreign_key_constraint(element, **kw)

“MATCH” 关键字实际上更加阴险,并且在与 MySQL 或 MariaDB 后端一起使用时,SQLAlchemy 明确禁止使用此参数。这个参数被 MySQL / MariaDB 默默地忽略,但此外,还会导致后端也忽略 ON UPDATE 和 ON DELETE 选项。因此,不应该在 MySQL / MariaDB 后端使用 MATCH;与 DEFERRABLE 和 INITIALLY 一样,可以使用自定义编译规则来在 DDL 定义时纠正 ForeignKeyConstraint。

外键约束的反射

并非所有的 MySQL / MariaDB 存储引擎都支持外键。在使用非常常见的 MyISAM MySQL 存储引擎时,通过表反射加载的信息将不包括外键。对于这些表,可以在反射时提供 ForeignKeyConstraint

Table('mytable', metadata,
      ForeignKeyConstraint(['other_id'], ['othertable.other_id']),
      autoload_with=engine
     )

另请参阅

包括存储引擎的 CREATE TABLE 参数

MySQL / MariaDB 唯一约束和反射

SQLAlchemy 支持带有标志 unique=TrueIndex 构造,表示唯一索引,以及表示唯一约束的 UniqueConstraint 构造。当发出 DDL 以创建这些约束时,MySQL / MariaDB 支持这两种对象/语法。然而,MySQL / MariaDB 没有与唯一索引分离的唯一约束构造;也就是说,在 MySQL / MariaDB 上,“UNIQUE” 约束等效于创建一个 “UNIQUE INDEX”。

当反射这些结构时,Inspector.get_indexes()Inspector.get_unique_constraints() 方法都会在 MySQL / MariaDB 中为唯一索引返回一个条目。然而,当使用 Table(..., autoload_with=engine) 进行完整表反射时,UniqueConstraint 结构在任何情况下都不是完全反映的 Table 结构的一部分;这个结构总是由在 Table.indexes 集合中存在 unique=True 设置的 Index 表示。

TIMESTAMP / DATETIME 问题

为 MySQL / MariaDB 的 explicit_defaults_for_timestamp 渲染 ON UPDATE CURRENT TIMESTAMP

MySQL / MariaDB 在历史上将 TIMESTAMP 数据类型的 DDL 扩展为短语 “TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP”,其中包含非标准 SQL,当发生 UPDATE 时自动更新列为当前时间戳,消除了在需要服务器端更新更改时通常需��使用触发器的情况。

MySQL 5.6 引入了一个新标志 explicit_defaults_for_timestamp,它禁用了上述行为,在 MySQL 8 中,该标志默认为 true,这意味着为了获得一个 MySQL 的 “on update timestamp” 而不改变这个标志,上述 DDL 必须显式地渲染。此外,相同的 DDL 也适用于 DATETIME 数据类型。

SQLAlchemy 的 MySQL 方言目前还没有选项来生成 MySQL 的 “ON UPDATE CURRENT_TIMESTAMP” 子句,需要注意这不是一个通用的 “ON UPDATE”,因为标准 SQL 中没有这样的语法。SQLAlchemy 的 Column.server_onupdate 参数目前与这种特殊的 MySQL 行为无关。

要生成这个 DDL,请使用 Column.server_default 参数,并传递一个包含 ON UPDATE 子句的文本子句:

from sqlalchemy import Table, MetaData, Column, Integer, String, TIMESTAMP
from sqlalchemy import text

metadata = MetaData()

mytable = Table(
    "mytable",
    metadata,
    Column('id', Integer, primary_key=True),
    Column('data', String(50)),
    Column(
        'last_updated',
        TIMESTAMP,
        server_default=text("CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP")
    )
)

使用DateTimeDATETIME数据类型也适用相同的说明:

from sqlalchemy import DateTime

mytable = Table(
    "mytable",
    metadata,
    Column('id', Integer, primary_key=True),
    Column('data', String(50)),
    Column(
        'last_updated',
        DateTime,
        server_default=text("CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP")
    )
)

即使Column.server_onupdate功能不生成此 DDL,但仍然希望向 ORM 发出信号,表明应该获取此更新值。此语法如下所示:

from sqlalchemy.schema import FetchedValue

class MyClass(Base):
    __tablename__ = 'mytable'

    id = Column(Integer, primary_key=True)
    data = Column(String(50))
    last_updated = Column(
        TIMESTAMP,
        server_default=text("CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP"),
        server_onupdate=FetchedValue()
    )
```  ### TIMESTAMP 列和 NULL

MySQL 历史上强制要求指定 TIMESTAMP 数据类型的列隐式包含 CURRENT_TIMESTAMP 的默认值,即使没有明确说明,还将列设置为 NOT NULL,这与所有其他数据类型的行为相反:

```py
mysql> CREATE TABLE ts_test (
    -> a INTEGER,
    -> b INTEGER NOT NULL,
    -> c TIMESTAMP,
    -> d TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    -> e TIMESTAMP NULL);
Query OK, 0 rows affected (0.03 sec)

mysql> SHOW CREATE TABLE ts_test;
+---------+-----------------------------------------------------
| Table   | Create Table
+---------+-----------------------------------------------------
| ts_test | CREATE TABLE `ts_test` (
  `a` int(11) DEFAULT NULL,
  `b` int(11) NOT NULL,
  `c` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  `d` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `e` timestamp NULL DEFAULT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1

如上所示,INTEGER 列默认为 NULL,除非指定为 NOT NULL。但是当列的类型为 TIMESTAMP 时,将生成一个 CURRENT_TIMESTAMP 的隐式默认值,这也会强制列为 NOT NULL,即使我们没有这样指定。

可以通过 MySQL 的 explicit_defaults_for_timestamp 配置标志在 MySQL 端更改此 MySQL 的行为,该标志在 MySQL 5.6 中引入。启用此服务器设置后,TIMESTAMP 列在 MySQL 端与默认值和可空性方面的行为与任何其他数据类型相同。

但是,为了适应大多数不指定此新标志的 MySQL 数据库,SQLAlchemy 会在不指定nullable=False的任何 TIMESTAMP 列中显式发出“NULL”说明符。为了适应指定了explicit_defaults_for_timestamp的较新数据库,SQLAlchemy 还会为指定了nullable=False的 TIMESTAMP 列发出 NOT NULL。以下示例说明了这一点:

from sqlalchemy import MetaData, Integer, Table, Column, text
from sqlalchemy.dialects.mysql import TIMESTAMP

m = MetaData()
t = Table('ts_test', m,
        Column('a', Integer),
        Column('b', Integer, nullable=False),
        Column('c', TIMESTAMP),
        Column('d', TIMESTAMP, nullable=False)
    )

from sqlalchemy import create_engine
e = create_engine("mysql+mysqldb://scott:tiger@localhost/test", echo=True)
m.create_all(e)

输出:

CREATE TABLE ts_test (
    a INTEGER,
    b INTEGER NOT NULL,
    c TIMESTAMP NULL,
    d TIMESTAMP NOT NULL
)
```  ### 为 MySQL / MariaDB 的 explicit_defaults_for_timestamp 渲染 ON UPDATE CURRENT TIMESTAMP

MySQL / MariaDB 历史上扩展了 DDL,将`TIMESTAMP`数据类型扩展为“TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP”,其中包含非标准的 SQL,当发生 UPDATE 时自动更新列为当前时间戳,从而消除了在需要服务器端更新更改的情况下使用触发器的常规需求。

MySQL 5.6 引入了一个新标志[explicit_defaults_for_timestamp](https://dev.mysql.com/doc/refman/5.6/en/server-system-variables.html#sysvar_explicit_defaults_for_timestamp),禁用了上述行为,在 MySQL 8 中,此标志默认为 true,这意味着为了获得 MySQL 的“on update timestamp”而不更改此标志,必须显式呈现上述 DDL。此外,对于 DATETIME 数据类型,相同的 DDL 也是有效的。

SQLAlchemy 的 MySQL 方言目前还没有选项来生成 MySQL 的“ON UPDATE CURRENT_TIMESTAMP”子句,需要注意的是这不是一个通用的“ON UPDATE”,因为标准 SQL 中没有这样的语法。SQLAlchemy 的`Column.server_onupdate`参数目前与这种特殊的 MySQL 行为无关。

要生成这个 DDL,请使用`Column.server_default`参数,并传递一个包含 ON UPDATE 子句的文本子句:

```py
from sqlalchemy import Table, MetaData, Column, Integer, String, TIMESTAMP
from sqlalchemy import text

metadata = MetaData()

mytable = Table(
    "mytable",
    metadata,
    Column('id', Integer, primary_key=True),
    Column('data', String(50)),
    Column(
        'last_updated',
        TIMESTAMP,
        server_default=text("CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP")
    )
)

同样的指令适用于使用DateTimeDATETIME数据类型的情况:

from sqlalchemy import DateTime

mytable = Table(
    "mytable",
    metadata,
    Column('id', Integer, primary_key=True),
    Column('data', String(50)),
    Column(
        'last_updated',
        DateTime,
        server_default=text("CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP")
    )
)

即使Column.server_onupdate特性不生成这个 DDL,仍然有必要向 ORM 发出信号,表明应该获取这个更新后的值。语法如下所示:

from sqlalchemy.schema import FetchedValue

class MyClass(Base):
    __tablename__ = 'mytable'

    id = Column(Integer, primary_key=True)
    data = Column(String(50))
    last_updated = Column(
        TIMESTAMP,
        server_default=text("CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP"),
        server_onupdate=FetchedValue()
    )

TIMESTAMP 列和 NULL

MySQL 在历史上规定,指定 TIMESTAMP 数据类型的列隐含地包含了 CURRENT_TIMESTAMP 的默认值,即使没有明确说明,并且还将该列设置为 NOT NULL,与所有其他数据类型相反的行为:

mysql> CREATE TABLE ts_test (
    -> a INTEGER,
    -> b INTEGER NOT NULL,
    -> c TIMESTAMP,
    -> d TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    -> e TIMESTAMP NULL);
Query OK, 0 rows affected (0.03 sec)

mysql> SHOW CREATE TABLE ts_test;
+---------+-----------------------------------------------------
| Table   | Create Table
+---------+-----------------------------------------------------
| ts_test | CREATE TABLE `ts_test` (
  `a` int(11) DEFAULT NULL,
  `b` int(11) NOT NULL,
  `c` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  `d` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `e` timestamp NULL DEFAULT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1

在上面的例子中,我们看到一个 INTEGER 列默认为 NULL,除非指定为 NOT NULL。但是当列的类型为 TIMESTAMP 时,会生成一个隐含的默认值 CURRENT_TIMESTAMP,这也会强制将列设置为 NOT NULL,即使我们没有明确指定。

MySQL 的这种行为可以通过 MySQL 端使用 MySQL 5.6 引入的explicit_defaults_for_timestamp配置标志来更改。启用此服务器设置后,TIMESTAMP 列在 MySQL 端的默认值和可空性方面的行为与任何其他数据类型相同。

然而,为了适应大多数不指定此新标志的 MySQL 数据库,SQLAlchemy 对于不指定 nullable=False 的任何 TIMESTAMP 列都显式地发出“NULL”指定符。为了适应指定了 nullable=False 的 TIMESTAMP 列的新数据库,SQLAlchemy 还为这些列发出 NOT NULL。以下示例说明了这一点:

from sqlalchemy import MetaData, Integer, Table, Column, text
from sqlalchemy.dialects.mysql import TIMESTAMP

m = MetaData()
t = Table('ts_test', m,
        Column('a', Integer),
        Column('b', Integer, nullable=False),
        Column('c', TIMESTAMP),
        Column('d', TIMESTAMP, nullable=False)
    )

from sqlalchemy import create_engine
e = create_engine("mysql+mysqldb://scott:tiger@localhost/test", echo=True)
m.create_all(e)

输出:

CREATE TABLE ts_test (
    a INTEGER,
    b INTEGER NOT NULL,
    c TIMESTAMP NULL,
    d TIMESTAMP NOT NULL
)

MySQL SQL 构造

对象名称 描述
match 生成 MATCH (X, Y) AGAINST ('TEXT') 子句。
class sqlalchemy.dialects.mysql.match

生成 MATCH (X, Y) AGAINST ('TEXT') 子句。

例如:

from sqlalchemy import desc
from sqlalchemy.dialects.mysql import match

match_expr = match(
    users_table.c.firstname,
    users_table.c.lastname,
    against="Firstname Lastname",
)

stmt = (
    select(users_table)
    .where(match_expr.in_boolean_mode())
    .order_by(desc(match_expr))
)

将产生类似于以下的 SQL:

SELECT id, firstname, lastname
FROM user
WHERE MATCH(firstname, lastname) AGAINST (:param_1 IN BOOLEAN MODE)
ORDER BY MATCH(firstname, lastname) AGAINST (:param_2) DESC

match() 函数是所有 SQL 表达式上都可用的 ColumnElement.match() 方法的独立版本,与使用 ColumnElement.match() 时相同,但允许传递多个列

参数:

  • cols – 要匹配的列表达式

  • against – 要比较的表达式

  • in_boolean_mode – 布尔值,将“布尔模式”设置为真

  • in_natural_language_mode – 布尔值,将“自然语言”设置为真

  • with_query_expansion – 布尔值,将“查询扩展”设置为真

版本 1.4.19 中新增。

另请参阅

ColumnElement.match()

成员

in_boolean_mode(), in_natural_language_mode(), inherit_cache, with_query_expansion()

类签名

sqlalchemy.dialects.mysql.match (sqlalchemy.sql.expression.Generative, sqlalchemy.sql.expression.BinaryExpression)

method in_boolean_mode() → Self

将“IN BOOLEAN MODE”修饰符应用于 MATCH 表达式。

返回:

带有修改的新 match 实例。

method in_natural_language_mode() → Self

将“IN NATURAL LANGUAGE MODE”修饰符应用于 MATCH 表达式。

返回:

带有修改的新 match 实例。

attribute inherit_cache: bool | None = True

指示此 HasCacheKey 实例是否应使用其直接超类使用的缓存键生成方案。

该属性默认为 None,表示构造尚未考虑其是否适合参与缓存;这在功能上等效于将值设置为 False,只是还会发出警告。

如果 SQL 与对象对应的类没有基于该类本地属性而不是其超类发生变化,则可以将此标志设置为 True

另请参阅

为自定义构造启用缓存支持 - 设置第三方或用户定义的 SQL 构造的 HasCacheKey.inherit_cache 属性的一般指南。

method with_query_expansion() → Self

对 MATCH 表达式应用 “WITH QUERY EXPANSION” 修饰符。

返回:

应用了修改的 match 实例。

MySQL 数据类型

与所有 SQLAlchemy 方言一样,已知与 MySQL 有效的所有大写类型都可以从顶级方言导入:

from sqlalchemy.dialects.mysql import (
    BIGINT,
    BINARY,
    BIT,
    BLOB,
    BOOLEAN,
    CHAR,
    DATE,
    DATETIME,
    DECIMAL,
    DECIMAL,
    DOUBLE,
    ENUM,
    FLOAT,
    INTEGER,
    LONGBLOB,
    LONGTEXT,
    MEDIUMBLOB,
    MEDIUMINT,
    MEDIUMTEXT,
    NCHAR,
    NUMERIC,
    NVARCHAR,
    REAL,
    SET,
    SMALLINT,
    TEXT,
    TIME,
    TIMESTAMP,
    TINYBLOB,
    TINYINT,
    TINYTEXT,
    VARBINARY,
    VARCHAR,
    YEAR,
)

MySQL 特有的类型,或具有特定于 MySQL 的构造参数的类型如下:

对象名称 描述
BIGINT MySQL BIGINTEGER 类型。
BIT MySQL BIT 类型。
CHAR MySQL CHAR 类型,用于固定长度的字符数据。
DATETIME MySQL DATETIME 类型。
DECIMAL MySQL DECIMAL 类型。
ENUM MySQL ENUM 类型。
FLOAT MySQL FLOAT 类型。
INTEGER MySQL INTEGER 类型。
JSON MySQL JSON 类型。
LONGBLOB MySQL LONGBLOB 类型,用于最多 2³² 字节的二进制数据。
LONGTEXT MySQL LONGTEXT 类型,用于最多编码为 2³² 字节的字符存储。
MEDIUMBLOB MySQL MEDIUMBLOB 类型,用于最多 2²⁴ 字节的二进制数据。
MEDIUMINT MySQL MEDIUMINTEGER 类型。
MEDIUMTEXT MySQL MEDIUMTEXT 类型,用于最多编码为 2²⁴ 字节的字符存储。
NCHAR MySQL NCHAR 类型。
NUMERIC MySQL NUMERIC 类型。
NVARCHAR MySQL NVARCHAR 类型。
REAL MySQL REAL 类型。
SET MySQL SET 类型。
SMALLINT MySQL SMALLINTEGER 类型。
TIME MySQL TIME 类型。
TIMESTAMP MySQL TIMESTAMP 类型。
TINYBLOB MySQL TINYBLOB 类型,用于存储最多 2⁸ 字节的二进制数据。
TINYINT MySQL TINYINT 类型。
TINYTEXT MySQL TINYTEXT 类型,用于存储编码最多 2⁸ 字节的字符数据。
VARCHAR MySQL VARCHAR 类型,用于存储可变长度的字符数据。
YEAR MySQL YEAR 类型,用于单字节存储 1901 年至 2155 年之间的年份。
class sqlalchemy.dialects.mysql.BIGINT

MySQL BIGINTEGER 类型。

成员

init()

类签名

class sqlalchemy.dialects.mysql.BIGINT (sqlalchemy.dialects.mysql.types._IntegerType, sqlalchemy.types.BIGINT)

method __init__(display_width=None, **kw)

构造一个 BIGINTEGER。

参数:

  • display_width – 可选,此数字的最大显示宽度。

  • unsigned – 一个布尔值,可选。

  • zerofill – 可选。如果为 true,则值将以左填充零的字符串形式存储。注意,这不会影响底层数据库 API 返回的值,其仍然是数值。

class sqlalchemy.dialects.mysql.BINARY

SQL BINARY 类型。

类签名

class sqlalchemy.dialects.mysql.BINARY (sqlalchemy.types._Binary)

class sqlalchemy.dialects.mysql.BIT

MySQL BIT 类型。

此类型适用于 MySQL 5.0.3 或更高版本的 MyISAM,并且适用于 5.0.5 或更高版本的 MyISAM、MEMORY、InnoDB 和 BDB。对于较旧版本,请使用 MSTinyInteger() 类型。

成员

init()

类签名

class sqlalchemy.dialects.mysql.BIT (sqlalchemy.types.TypeEngine)

method __init__(length=None)

构造一个 BIT。

参数:

length – 可选,位数。

class sqlalchemy.dialects.mysql.BLOB

SQL BLOB 类型。

类签名

class sqlalchemy.dialects.mysql.BLOB (sqlalchemy.types.LargeBinary)

method __init__(length: int | None = None)

继承自 LargeBinarysqlalchemy.types.LargeBinary.__init__ 方法

构造一个 LargeBinary 类型。

参数:

length – 可选,用于 DDL 语句中的列长度,适用于那些接受长度的二进制类型,比如 MySQL BLOB 类型。

class sqlalchemy.dialects.mysql.BOOLEAN

SQL BOOLEAN 类型。

类签名

class sqlalchemy.dialects.mysql.BOOLEAN (sqlalchemy.types.Boolean)

method __init__(create_constraint: bool = False, name: str | None = None, _create_events: bool = True, _adapted_from: SchemaType | None = None)

继承自 Booleansqlalchemy.types.Boolean.__init__ 方法

构造一个 Boolean。

参数:

  • create_constraint

    默认为 False。如果将布尔值生成为 int/smallint,则还在表上创建一个 CHECK 约束,以确保值为 1 或 0。

    注意

    强烈建议 CHECK 约束具有明确的名称,以支持模式管理问题。这可以通过设置 Boolean.name 参数或设置适当的命名约定来建立;有关背景信息,请参阅 配置约束命名约定。

    从版本 1.4 开始更改:- 此标志现在默认为 False,表示非本地枚举类型不生成 CHECK 约束。

  • name – 如果生成 CHECK 约束,请指定约束的名称。

class sqlalchemy.dialects.mysql.CHAR

MySQL CHAR 类型,用于固定长度的字符数据。

成员

init()

类签名

sqlalchemy.dialects.mysql.CHAR (sqlalchemy.dialects.mysql.types._StringType, sqlalchemy.types.CHAR)

method __init__(length=None, **kwargs)

构建一个 CHAR。

参数:

  • length – 最大数据长度,以字符为单位。

  • binary – 可选项,使用国家字符集的默认二进制排序规则。这不影响存储的数据类型,对于二进制数据,请使用 BINARY 类型。

  • collation – 可选项,请求特定排序规则。必须与国家字符集兼容。

class sqlalchemy.dialects.mysql.DATE

SQL DATE 类型。

类签名

sqlalchemy.dialects.mysql.DATE (sqlalchemy.types.Date)

class sqlalchemy.dialects.mysql.DATETIME

MySQL DATETIME 类型。

成员

init()

类签名

sqlalchemy.dialects.mysql.DATETIME (sqlalchemy.types.DATETIME)

method __init__(timezone=False, fsp=None)

构建一个 MySQL DATETIME 类型。

参数:

  • timezone – MySQL 方言不使用。

  • fsp

    小数秒精度值。MySQL 5.6.4 支持小数秒的存储;在发出 DATETIME 类型的 DDL 时将使用此参数。

    注意

    DBAPI 驱动程序对小数秒的支持可能有限;当前支持包括 MySQL Connector/Python。

class sqlalchemy.dialects.mysql.DECIMAL

MySQL DECIMAL 类型。

成员

init()

类签名

sqlalchemy.dialects.mysql.DECIMAL (sqlalchemy.dialects.mysql.types._NumericType, sqlalchemy.types.DECIMAL)

method __init__(precision=None, scale=None, asdecimal=True, **kw)

构建一个 DECIMAL。

参数:

  • precision – 此数字中的总位数。如果比例和精度都为 None,则值将存储到服务器允许的限制。

  • scale – 小数点后的位数。

  • unsigned – 一个布尔值,可选。

  • zerofill – 可选项。如果为真,则值将以左侧填充零的字符串形式存储。请注意,这不会影响底层数据库 API 返回的值,其仍然是数值。

class sqlalchemy.dialects.mysql.DOUBLE

MySQL DOUBLE 类型。

类签名

class sqlalchemy.dialects.mysql.DOUBLE (sqlalchemy.dialects.mysql.types._FloatType, sqlalchemy.types.DOUBLE)

method __init__(precision=None, scale=None, asdecimal=True, **kw)

构造一个 DOUBLE。

注意

默认情况下,DOUBLE 类型将从浮点数转换为 Decimal,使用默认的截断为 10 位数。指定 scale=ndecimal_return_scale=n 以更改此比例,或者 asdecimal=False 以直接返回 Python 浮点数值。

参数:

  • precision – 此数字中的总位数。如果 scale 和 precision 都为 None,则值将存储到服务器允许的限制。

  • scale – 小数点后的位数。

  • unsigned – 一个布尔值,可选。

  • zerofill – 可选项。如果为真,则值将以左侧填充零的字符串形式存储。请注意,这不会影响底层数据库 API 返回的值,其仍然是数值。

class sqlalchemy.dialects.mysql.ENUM

MySQL ENUM 类型。

成员

init()

类签名

class sqlalchemy.dialects.mysql.ENUM (sqlalchemy.types.NativeForEmulated, sqlalchemy.types.Enum, sqlalchemy.dialects.mysql.types._StringType)

method __init__(*enums, **kw)

构造一个 ENUM。

例如:

Column('myenum', ENUM("foo", "bar", "baz"))

参数:

  • enums

    此 ENUM 的有效值范围。在枚举中的值不带引号,生成模式时将被转义并用单引号括起。此对象也可以是符合 PEP-435 的枚举类型。

  • strict

    此标志无效。

    在版本更改:MySQL ENUM 类型以及基本 Enum 类型现在验证所有 Python 数据值。

  • charset – 可选项,用于此字符串值的列级字符集。优先于‘ascii’或‘unicode’简写。

  • collation – 可选项,用于此字符串值的列级排序。优先于‘binary’简写。

  • ascii – 默认为 False:latin1 字符集的简写,生成模式中的 ASCII。

  • unicode – 默认为 False:ucs2 字符集的简写,生成模式中的 UNICODE。

  • binary – 默认为 False:简写,选择与列的字符集匹配的二进制排序类型。在模式中生成 BINARY。这不会影响存储的数据类型,只会影响字符数据的排序。

class sqlalchemy.dialects.mysql.FLOAT

MySQL FLOAT 类型。

成员

init()

类签名

sqlalchemy.dialects.mysql.FLOAT (sqlalchemy.dialects.mysql.types._FloatType, sqlalchemy.types.FLOAT)

method __init__(precision=None, scale=None, asdecimal=False, **kw)

构造一个浮点数。

参数:

  • precision – 此数字中的总位数。如果 scale 和 precision 都为 None,则将值存储到服务器允许的限制。

  • scale – 小数点后的位数。

  • unsigned – 一个布尔值,可选。

  • zerofill – 可选。如果为 true,则值将存储为左填充的带有零的字符串。请注意,这不会影响底层数据库 API 返回的值,其仍然为数值。

class sqlalchemy.dialects.mysql.INTEGER

MySQL INTEGER 类型。

成员

init()

类签名

sqlalchemy.dialects.mysql.INTEGER (sqlalchemy.dialects.mysql.types._IntegerType, sqlalchemy.types.INTEGER)

method __init__(display_width=None, **kw)

构造一个整数。

参数:

  • display_width – 可选,此数字的最大显示宽度。

  • unsigned – 一个布尔值,可选。

  • zerofill – 可选。如果为 true,则值将存储为左填充的带有零的字符串。请注意,这不会影响底层数据库 API 返回的值,其仍然为数值。

class sqlalchemy.dialects.mysql.JSON

MySQL JSON 类型。

MySQL 从版本 5.7 开始支持 JSON。MariaDB 从版本 10.2 开始支持 JSON(作为 LONGTEXT 的别名)。

当基本的 JSON 数据类型与 MySQL 或 MariaDB 后端一起使用时,JSON 会自动使用。

另请参阅

JSON - 用于通用跨平台 JSON 数据类型的主要文档。

JSON 类型支持 JSON 值的持久性以及通过调整操作以在数据库级别呈现 JSON_EXTRACT 函数所提供的核心索引操作,从而适应基本的 JSON 数据类型。

类签名

sqlalchemy.dialects.mysql.JSONsqlalchemy.types.JSON

class sqlalchemy.dialects.mysql.LONGBLOB

MySQL LONGBLOB 类型,用于二进制数据最多 2³² 字节。

类签名

sqlalchemy.dialects.mysql.LONGBLOB (sqlalchemy.types._Binary)

class sqlalchemy.dialects.mysql.LONGTEXT

MySQL LONGTEXT 类型,用于字符存储编码最多 2³² 字节。

成员

init()

类签名

sqlalchemy.dialects.mysql.LONGTEXT (sqlalchemy.dialects.mysql.types._StringType)

method __init__(**kwargs)

构造一个 LONGTEXT。

参数:

  • charset – 可选,此字符串值的列级字符集。优先于‘ascii’或‘unicode’简写。

  • collation – 可选,此字符串值的列级校对。优先于‘binary’简写。

  • ascii – 默认为 False:latin1字符集的简写,生成模式中的 ASCII。

  • unicode – 默认为 False:ucs2字符集的简写,生成模式中的 UNICODE。

  • national – 可选。如果为 true,则使用服务器配置的国家字符集。

  • binary – 默认为 False:简写,选择与列的字符集匹配的二进制校对类型。在模式中生成 BINARY。这不影响存储的数据类型,只影响字符数据的校对。

class sqlalchemy.dialects.mysql.MEDIUMBLOB

MySQL MEDIUMBLOB 类型,用于二进制数据达到 2²⁴ 字节。

类签名

sqlalchemy.dialects.mysql.MEDIUMBLOB (sqlalchemy.types._Binary)

class sqlalchemy.dialects.mysql.MEDIUMINT

MySQL MEDIUMINTEGER 类型。

成员

init()

类签名

sqlalchemy.dialects.mysql.MEDIUMINT (sqlalchemy.dialects.mysql.types._IntegerType)

method __init__(display_width=None, **kw)

构造一个 MEDIUMINTEGER

参数:

  • display_width – 可选,此数字的最大显示宽度。

  • unsigned – 一个布尔值,可选。

  • zerofill – 可选。如果为 true,则值将以左边填充零的字符串形式存储。请注意,这不影响底层数据库 API 返回的值,其仍然是数值。

class sqlalchemy.dialects.mysql.MEDIUMTEXT

MySQL MEDIUMTEXT 类型,用于存储编码达到 2²⁴ 字节的字符。

成员

init()

类签名

sqlalchemy.dialects.mysql.MEDIUMTEXT (sqlalchemy.dialects.mysql.types._StringType)

method __init__(**kwargs)

构造一个 MEDIUMTEXT。

参数:

  • charset – 可选,此字符串值的列级字符集。优先于‘ascii’或‘unicode’简写。

  • collation – 可选,此字符串值的列级校对。优先于‘binary’简写。

  • ascii – 默认为 False:latin1字符集的简写,生成模式中的 ASCII。

  • unicode – 默认为 False:ucs2字符集的简写,生成模式中的 UNICODE。

  • national – 可选。如果为 true,则使用服务器配置的国家字符集。

  • binary – 默认为 False:简写,选择与列的字符集匹配的二进制校对类型。在模式中生成 BINARY。这不影响存储的数据类型,只影响字符数据的校对。

class sqlalchemy.dialects.mysql.NCHAR

MySQL NCHAR 类型。

用于服务器配置的国家字符集中的固定长度字符数据。

成员

init()

类签名

sqlalchemy.dialects.mysql.NCHARsqlalchemy.dialects.mysql.types._StringTypesqlalchemy.types.NCHAR

method __init__(length=None, **kwargs)

构造一个 NCHAR。

参数:

  • length – 最大数据长度,以字符为单位。

  • binary – 可选,使用国家字符集的默认二进制排序规则。这不会影响存储的数据类型,对于二进制数据,请使用 BINARY 类型。

  • collation – 可选,请求特定��排序规则。必须与国家字符集兼容。

class sqlalchemy.dialects.mysql.NUMERIC

MySQL NUMERIC 类型。

成员

init()

类签名

sqlalchemy.dialects.mysql.NUMERICsqlalchemy.dialects.mysql.types._NumericTypesqlalchemy.types.NUMERIC

method __init__(precision=None, scale=None, asdecimal=True, **kw)

构造一个 NUMERIC。

参数:

  • precision – 此数字中的总位数。如果比例和精度都为 None,则值将存储到服务器允许的限制。

  • scale – 小数点后的位数。

  • unsigned – 一个布尔值,可选。

  • zerofill – 可选。如果为 true,则值将以左侧填充零的字符串形式存储。请注意,这不会影响底层数据库 API 返回的值,其仍然是数值型的。

class sqlalchemy.dialects.mysql.NVARCHAR

MySQL NVARCHAR 类型。

用于服务器配置的国家字符集中的可变长度字符数据。

成员

init()

类签名

sqlalchemy.dialects.mysql.NVARCHARsqlalchemy.dialects.mysql.types._StringTypesqlalchemy.types.NVARCHAR

method __init__(length=None, **kwargs)

构造一个 NVARCHAR。

参数:

  • length – 最大数据长度,以字符为单位。

  • binary – 可选,使用国家字符集的默认二进制排序规则。这不会影响存储的数据类型,对于二进制数据,请使用 BINARY 类型。

  • collation – 可选,请求特定的排序规则。必须与国家字符集兼容。

class sqlalchemy.dialects.mysql.REAL

MySQL REAL 类型。

成员

init()

类签名

sqlalchemy.dialects.mysql.REALsqlalchemy.dialects.mysql.types._FloatTypesqlalchemy.types.REAL

method __init__(precision=None, scale=None, asdecimal=True, **kw)

构造一个 REAL。

注意

默认情况下,REAL 类型将从浮点数转换为 Decimal,使用默认为 10 位的截断。要更改此标度,请指定 scale=ndecimal_return_scale=n,或者指定 asdecimal=False 以直接将值返回为 Python 浮点数。

参数:

  • precision – 此数字中的总位数。如果 scale 和 precision 都为 None,则值将存储到服务器允许的限制。

  • scale – 小数点后的位数。

  • unsigned – 一个布尔值,可选。

  • zerofill – 可选。如果为真,则值将作为用零左填充的字符串存储。请注意,这不影响底层数据库 API 返回的值,这些值仍然是数字。

class sqlalchemy.dialects.mysql.SET

MySQL SET 类型。

成员

init()

类签名

sqlalchemy.dialects.mysql.SET (sqlalchemy.dialects.mysql.types._StringType)

method __init__(*values, **kw)

构造一个 SET。

例如:

Column('myset', SET("foo", "bar", "baz"))

在此 set 将用于为表生成 DDL 或者如果 SET.retrieve_as_bitwise 标志设置为 True,则必须提供潜在值的列表。

参数:

  • values – 此 SET 的有效值范围。这些值不加引号,生成模式时会被转义并用单引号括起来。

  • convert_unicode – 与 String.convert_unicode 相同的标志。

  • collation – 与 String.collation 相同。

  • charset – 与 VARCHAR.charset 相同。

  • ascii – 与 VARCHAR.ascii 相同。

  • unicode – 与 VARCHAR.unicode 相同。

  • binary – 与 VARCHAR.binary 相同。

  • retrieve_as_bitwise

    如果为 True,set 类型的数据将使用整数值进行持久化和选择,其中一个 set 被强制转换为位掩码进行持久化。MySQL 允许此模式,它的优点是能够明确地存储值,如空字符串 ''。在 SELECT 语句中,数据类型将显示为表达式 col + 0,以便值被强制转换为整数值在结果集中返回。如果希望持久化一个可以存储空字符串 '' 作为值的 set,则需要此标志。

    警告

    在使用 SET.retrieve_as_bitwise 时,重要的是确保集合值的列表与 MySQL 数据库中存在的完全相同的顺序

class sqlalchemy.dialects.mysql.SMALLINT

MySQL SMALLINTEGER 类型。

成员

init()

类签名

sqlalchemy.dialects.mysql.SMALLINT (sqlalchemy.dialects.mysql.types._IntegerType, sqlalchemy.types.SMALLINT)

method __init__(display_width=None, **kw)

构造一个 SMALLINTEGER。

参数:

  • display_width – 可选,此数字的最大显示宽度。

  • unsigned – 一个布尔值,可选。

  • zerofill – 可选。如果为真,则值将作为用零填充的字符串存储。请注意,这不影响底层数据库 API 返回的值,它们仍然是数字。

class sqlalchemy.dialects.mysql.TEXT

MySQL TEXT 类型,用于存储编码为最多 2¹⁶ 字节的字符。

类签名

sqlalchemy.dialects.mysql.TEXT (sqlalchemy.dialects.mysql.types._StringType, sqlalchemy.types.TEXT)

method __init__(length=None, **kw)

构造一个 TEXT。

参数:

  • length – 可选,如果提供了,服务器可以通过用足够存储 length 字节字符的最小 TEXT 类型替换来优化存储。

  • charset – 可选,此字符串值的列级字符集。优先于 ‘ascii’ 或 ‘unicode’ 简写。

  • collation – 可选,此字符串值的列级排序。优先于 ‘binary’ 简写。

  • ascii – 默认为 False:latin1 字符集的简写,生成模式中的 ASCII。

  • unicode – 默认为 False:ucs2 字符集的简写,生成模式中的 UNICODE。

  • national – 可选。如果为真,则使用服务器配置的国家字符集。

  • binary – 默认为 False:简写,选择与列的字符集匹配的二进制排序类型。在模式中生成 BINARY。这不影响存储的数据类型,只影响字符数据的排序。

class sqlalchemy.dialects.mysql.TIME

MySQL TIME 类型。

成员

init()

类签名

sqlalchemy.dialects.mysql.TIME (sqlalchemy.types.TIME)

method __init__(timezone=False, fsp=None)

构造一个 MySQL TIME 类型。

参数:

  • timezone – MySQL 方言不使用。

  • fsp

    分数秒精度值。MySQL 5.6 支持存储分数秒;在发出 TIME 类型的 DDL 时将使用此参数。

    注意

    DBAPI 驱动程序对于分数秒的支持可能有限;当前支持包括 MySQL Connector/Python。

class sqlalchemy.dialects.mysql.TIMESTAMP

MySQL TIMESTAMP 类型。

成员

init()

类签名

sqlalchemy.dialects.mysql.TIMESTAMP (sqlalchemy.types.TIMESTAMP)

method __init__(timezone=False, fsp=None)

构造一个 MySQL TIMESTAMP 类型。

参数:

  • timezone – MySQL 方言不使用。

  • fsp

    分数秒精度值。MySQL 5.6.4 支持分数秒的存储;在为 TIMESTAMP 类型发出 DDL 时将使用此参数。

    注意

    DBAPI 驱动程序对分数秒的支持可能有限;当前支持包括 MySQL Connector/Python。

class sqlalchemy.dialects.mysql.TINYBLOB

MySQL TINYBLOB 类型,用于存储最多 2⁸ 字节的二进制数据。

类签名

sqlalchemy.dialects.mysql.TINYBLOB (sqlalchemy.types._Binary)

class sqlalchemy.dialects.mysql.TINYINT

MySQL TINYINT 类型。

成员

init()

类签名

sqlalchemy.dialects.mysql.TINYINT (sqlalchemy.dialects.mysql.types._IntegerType)

method __init__(display_width=None, **kw)

构造一个 TINYINT。

参数:

  • display_width – 可选,此数字的最大显示宽度。

  • unsigned – 一个布尔值,可选。

  • zerofill – 可选。如果为 true,则值将存储为左填充零的字符串。注意,这不会影响底层数据库 API 返回的值,这些值仍然是数字。

class sqlalchemy.dialects.mysql.TINYTEXT

MySQL TINYTEXT 类型,用于存储编码为 2⁸ 字节的字符。

成员

init()

类签名

sqlalchemy.dialects.mysql.TINYTEXT (sqlalchemy.dialects.mysql.types._StringType)

method __init__(**kwargs)

构造一个 TINYTEXT。

参数:

  • charset – 可选,此字符串值的列级字符集。优先于 ‘ascii’ 或 ‘unicode’ 简写。

  • collation – 可选,此字符串值的列级排序。优先于 ‘binary’ 简写。

  • ascii – 默认为 False:latin1 字符集的简写,在模式中生成 ASCII。

  • unicode – 默认为 False:ucs2 字符集的简写,在模式中生成 UNICODE。

  • national – 可选。如果为 true,则使用服务器配置的国家字符集。

  • binary – 默认为 False:简写,选择与列的字符集匹配的二进制排序类型。在模式中生成 BINARY。这不影响存储的数据类型,只影响字符数据的排序。

class sqlalchemy.dialects.mysql.VARBINARY

SQL VARBINARY 类型。

类签名

sqlalchemy.dialects.mysql.VARBINARY (sqlalchemy.types._Binary)

class sqlalchemy.dialects.mysql.VARCHAR

MySQL VARCHAR 类型,用于可变长度字符数据。

成员

init()

类签名

sqlalchemy.dialects.mysql.VARCHAR (sqlalchemy.dialects.mysql.types._StringType, sqlalchemy.types.VARCHAR)

method __init__(length=None, **kwargs)

构造一个 VARCHAR。

参数:

  • charset – 可选,此字符串值的列级字符集。优先于‘ascii’或‘unicode’的简写形式。

  • collation – 可选,此字符串值的列级排序规则。优先于‘binary’的简写形式。

  • ascii – 默认为 False:latin1字符集的简写形式,在模式中生成 ASCII。

  • unicode – 默认为 False:ucs2字符集的简写形式,在模式中生成 UNICODE。

  • national – 可选。如果为 true,则使用服务器配置的国家字符集。

  • binary – 默认为 False:简写形式,选择与列的字符集匹配的二进制排序类型。在模式中生成 BINARY。这不影响存储的数据类型,只影响字符数据的排序规则。

class sqlalchemy.dialects.mysql.YEAR

MySQL YEAR 类型,用于存储 1901-2155 年的单字节。

类签名

sqlalchemy.dialects.mysql.YEAR (sqlalchemy.types.TypeEngine)

MySQL DML 构造

对象名称 描述
插入(表) 构造一个 MySQL/MariaDB 特定变体的Insert构造。
插入 INSERT 的 MySQL 特定实现。
function sqlalchemy.dialects.mysql.insert(table: _DMLTableArgument) → Insert

构造一个 MySQL/MariaDB 特定变体的Insert构造。

sqlalchemy.dialects.mysql.insert()函数创建一个sqlalchemy.dialects.mysql.Insert。这个类基于方言不可知的Insert构造,可以使用 SQLAlchemy Core 中的insert()函数构造。

Insert构造包括额外的方法Insert.on_duplicate_key_update()

class sqlalchemy.dialects.mysql.Insert

INSERT 的 MySQL 特定实现。

添加用于 MySQL 特定语法的方法,如 ON DUPLICATE KEY UPDATE。

Insert对象是使用sqlalchemy.dialects.mysql.insert()函数创建的。

版本 1.2 中的新功能。

成员

inherit_cache, inserted, on_duplicate_key_update()

类签名

sqlalchemy.dialects.mysql.Insert (sqlalchemy.sql.expression.Insert)

attribute inherit_cache: bool | None = False

指示此HasCacheKey实例是否应使用其直接超类使用的缓存键生成方案。

该属性默认为None,表示构造尚未考虑其是否适合参与缓存;这在功能上等同于将值设置为False,只是还会发出警告。

如果与该对象对应的 SQL 不基于此类的本地属性而是其超类,则可以在特定类上将此标志设置为True

另请参阅

为自定义构造启用缓存支持 - 为第三方或用户定义的 SQL 构造设置HasCacheKey.inherit_cache属性的一般指南。

attribute inserted

为 ON DUPLICATE KEY UPDATE 语句提供“inserted”命名空间

MySQL 的 ON DUPLICATE KEY UPDATE 子句允许引用将要插入的行,通过一个名为VALUES()的特殊函数。此属性提供了此行中的所有列可引用,以便它们在 ON DUPLICATE KEY UPDATE 子句中的VALUES()函数内呈现。该属性命名为.inserted,以避免与现有的Insert.values()方法发生冲突。

提示

Insert.inserted 属性是 ColumnCollection 的实例,提供了与 访问表和列 中描述的 Table.c 集合相同的接口。使用此集合,可以像属性一样访问普通名称(例如 stmt.inserted.some_column),但应该使用索引访问特殊名称和字典方法名称,例如 stmt.inserted["column name"]stmt.inserted["values"]。有关更多示例,请参阅 ColumnCollection 的文档字符串。

另请参阅

INSERT…ON DUPLICATE KEY UPDATE(插入或更新) - 使用 Insert.inserted 的示例

method on_duplicate_key_update(*args: Mapping[Any, Any] | List[Tuple[str, Any]] | ColumnCollection[Any, Any], **kw: Any) → Self

指定 ON DUPLICATE KEY UPDATE 子句。

参数:

**kw – 与 UPDATE 值关联的列键。值可以是任何 SQL 表达式或支持的字面 Python 值。

警告

此字典不会考虑 Python 指定的默认 UPDATE 值或生成函数,例如那些使用 Column.onupdate 指定的值。除非在此处手动指定值,否则这些值将不会被用于 ON DUPLICATE KEY UPDATE 风格的 UPDATE。

参数:

*args

作为传递键/值参数的替代方法,可以将字典或 2 元组的列表作为单个位置参数传递。

传递单个字典相当于关键字参数形式:

insert().on_duplicate_key_update({"name": "some name"})

传递 2 元组的列表表示 UPDATE 子句中的参数分配应按发送顺序排序,类似于整体描述的 Update 构造中的 参数排序更新:

insert().on_duplicate_key_update(
    [("name", "some name"), ("value", "some value")])

自 1.3 版更改:参数可以指定为字典或 2 元组的列表;后一种形式提供了参数的排序。

自 1.2 版新功能。

另请参阅

INSERT…ON DUPLICATE KEY UPDATE(插入或更新)

mysqlclient(MySQL-Python 的分支)

通过 mysqlclient(MySQL-Python 的维护分支)驱动程序支持 MySQL / MariaDB 数据库。

DBAPI

mysqlclient(MySQL-Python 的维护分支)的文档和下载信息(如果适用)可在此处找到:pypi.org/project/mysqlclient/

连接中

连接字符串:

mysql+mysqldb://<user>:<password>@<host>[:<port>]/<dbname>

驱动程序状态

mysqlclient DBAPI 是MySQL-Python DBAPI 的维护分支,后者已不再维护。mysqlclient支持 Python 2 和 Python 3,并且非常稳定。

Unicode

请参阅 Unicode 以获取有关 Unicode 处理的当前建议。### SSL Connections

mysqlclient 和 PyMySQL DBAPI 接受一个额外的字典,键为“ssl”,可以使用create_engine.connect_args字典指定:

engine = create_engine(
    "mysql+mysqldb://scott:tiger@192.168.0.134/test",
    connect_args={
        "ssl": {
            "ca": "/home/gord/client-ssl/ca.pem",
            "cert": "/home/gord/client-ssl/client-cert.pem",
            "key": "/home/gord/client-ssl/client-key.pem"
        }
    }
)

为方便起见,以下键也可以内联在 URL 中指定,它们将自动解释为“ssl”字典中: “ssl_ca”,“ssl_cert”,“ssl_key”,“ssl_capath”,“ssl_cipher”,“ssl_check_hostname”。示例如下:

connection_uri = (
    "mysql+mysqldb://scott:tiger@192.168.0.134/test"
    "?ssl_ca=/home/gord/client-ssl/ca.pem"
    "&ssl_cert=/home/gord/client-ssl/client-cert.pem"
    "&ssl_key=/home/gord/client-ssl/client-key.pem"
)

另请参阅

SSL Connections 在 PyMySQL 方言中

使用 MySQLdb 与 Google Cloud SQL

Google Cloud SQL 现在建议使用 MySQLdb 方言。使用以下 URL 进行连接:

mysql+mysqldb://root@/<dbname>?unix_socket=/cloudsql/<projectid>:<instancename>

服务器端游标

mysqldb 方言支持服务器端游标。请参阅 Server Side Cursors。

DBAPI

mysqlclient(MySQL-Python 的维护分支)的文档和下载信息(如果适用)可在以下链接找到:pypi.org/project/mysqlclient/

连接中

连接字符串:

mysql+mysqldb://<user>:<password>@<host>[:<port>]/<dbname>

驱动程序状态

mysqlclient DBAPI 是MySQL-Python DBAPI 的维护分支,后者已不再维护。mysqlclient支持 Python 2 和 Python 3,并且非常稳定。

Unicode

请参阅 Unicode 以获取有关 Unicode 处理的当前建议。

SSL Connections

mysqlclient 和 PyMySQL DBAPI 接受一个额外的字典,键为“ssl”,可以使用create_engine.connect_args字典指定:

engine = create_engine(
    "mysql+mysqldb://scott:tiger@192.168.0.134/test",
    connect_args={
        "ssl": {
            "ca": "/home/gord/client-ssl/ca.pem",
            "cert": "/home/gord/client-ssl/client-cert.pem",
            "key": "/home/gord/client-ssl/client-key.pem"
        }
    }
)

为方便起见,以下键也可以内联在 URL 中指定,它们将自动解释为“ssl”字典中: “ssl_ca”,“ssl_cert”,“ssl_key”,“ssl_capath”,“ssl_cipher”,“ssl_check_hostname”。示例如下:

connection_uri = (
    "mysql+mysqldb://scott:tiger@192.168.0.134/test"
    "?ssl_ca=/home/gord/client-ssl/ca.pem"
    "&ssl_cert=/home/gord/client-ssl/client-cert.pem"
    "&ssl_key=/home/gord/client-ssl/client-key.pem"
)

另请参阅

SSL Connections 在 PyMySQL 方言中

使用 MySQLdb 与 Google Cloud SQL

Google Cloud SQL 现在建议使用 MySQLdb 方言。使用以下 URL 进行连接:

mysql+mysqldb://root@/<dbname>?unix_socket=/cloudsql/<projectid>:<instancename>

服务器端游标

mysqldb 方言支持服务器端游标。请参阅 Server Side Cursors。

PyMySQL

通过 PyMySQL 驱动程序支持 MySQL / MariaDB 数据库。

DBAPI

PyMySQL 的文档和下载信息(如果适用)可在以下链接找到:pymysql.readthedocs.io/

连接中

连接字符串:

mysql+pymysql://<username>:<password>@<host>/<dbname>[?<options>]

Unicode

请参阅 Unicode 以获取有关 Unicode 处理的当前建议。

SSL 连接

PyMySQL DBAPI 接受与 MySQLdb 相同���SSL 参数,详见 SSL 连接。请参阅该部分以获取其他示例。

如果服务器使用自动生成的自签名证书或与主机名不匹配(从客户端看),还可能需要在 PyMySQL 中指定ssl_check_hostname=false

connection_uri = (
    "mysql+pymysql://scott:tiger@192.168.0.134/test"
    "?ssl_ca=/home/gord/client-ssl/ca.pem"
    "&ssl_cert=/home/gord/client-ssl/client-cert.pem"
    "&ssl_key=/home/gord/client-ssl/client-key.pem"
    "&ssl_check_hostname=false"
)

MySQL-Python 兼容性

pymysql DBAPI 是 MySQL-python(MySQLdb)驱动程序的纯 Python 移植版本,目标是 100%兼容。对于 MySQL-python 的大多数行为注意事项也适用于 pymysql 驱动程序。

DBAPI

PyMySQL 的文档和下载信息(如果适用)可在此处找到:pymysql.readthedocs.io/

连接

连接字符串:

mysql+pymysql://<username>:<password>@<host>/<dbname>[?<options>]

Unicode

有关当前有关 Unicode 处理的建议,请参阅 Unicode。

SSL 连接

PyMySQL DBAPI 接受与 MySQLdb 相同的 SSL 参数,详见 SSL 连接。请参阅该部分以获取其他示例。

如果服务器使用自动生成的自签名证书或与主机名不匹配(从客户端看),还可能需要在 PyMySQL 中指定ssl_check_hostname=false

connection_uri = (
    "mysql+pymysql://scott:tiger@192.168.0.134/test"
    "?ssl_ca=/home/gord/client-ssl/ca.pem"
    "&ssl_cert=/home/gord/client-ssl/client-cert.pem"
    "&ssl_key=/home/gord/client-ssl/client-key.pem"
    "&ssl_check_hostname=false"
)

MySQL-Python 兼容性

pymysql DBAPI 是 MySQL-python(MySQLdb)驱动程序的纯 Python 移植版本,目标是 100%兼容。对于 MySQL-python 的大多数行为注意事项也适用于 pymysql 驱动程序。

MariaDB-连接器

通过 MariaDB Connector/Python 驱动程序支持 MySQL / MariaDB 数据库。

DBAPI

MariaDB Connector/Python 的文档和下载信息(如果适用)可在此处找到:pypi.org/project/mariadb/

连接

连接字符串:

mariadb+mariadbconnector://<user>:<password>@<host>[:<port>]/<dbname>

驱动程序状态

MariaDB Connector/Python 使 Python 程序能够使用符合 Python DB API 2.0(PEP-249)的 API 访问 MariaDB 和 MySQL 数据库。它是用 C 编写的,并使用 MariaDB Connector/C 客户端库进行客户端服务器通信。

请注意,mariadb://连接 URI 的默认驱动程序仍然是mysqldb。要使用此驱动程序,需要mariadb+mariadbconnector://

DBAPI

MariaDB Connector/Python 的文档和下载信息(如果适用)可在此处找到:pypi.org/project/mariadb/

连接

连接字符串:

mariadb+mariadbconnector://<user>:<password>@<host>[:<port>]/<dbname>

驱动程序状态

MariaDB Connector/Python 使 Python 程序能够使用符合 Python DB API 2.0(PEP-249)的 API 访问 MariaDB 和 MySQL 数据库。它是用 C 编写的,并使用 MariaDB Connector/C 客户端库进行客户端服务器通信。

请注意,mariadb://连接 URI 的默认驱动程序仍然是mysqldb。要使用此驱动程序,需要mariadb+mariadbconnector://

MySQL-连接器

通过 MySQL Connector/Python 驱动程序支持 MySQL/MariaDB 数据库。

DBAPI

MySQL Connector/Python 的文档和下载信息(如果适用)可在此处获取:pypi.org/project/mysql-connector-python/

连接

连接字符串:

mysql+mysqlconnector://<user>:<password>@<host>[:<port>]/<dbname>

注意

自发布以来,MySQL Connector/Python DBAPI 存在许多问题,其中一些可能仍未解决,而 mysqlconnector 方言未作为 SQLAlchemy 持续集成的一部分进行测试。推荐的 MySQL 方言是 mysqlclient 和 PyMySQL。

DBAPI

MySQL Connector/Python 的文档和下载信息(如果适用)可在此处获取:pypi.org/project/mysql-connector-python/

连接

连接字符串:

mysql+mysqlconnector://<user>:<password>@<host>[:<port>]/<dbname>

asyncmy

通过 asyncmy 驱动程序支持 MySQL/MariaDB 数据库。

DBAPI

异步 MySQL 的文档和下载信息(如果适用)可在此处获取:github.com/long2ice/asyncmy

连接

连接字符串:

mysql+asyncmy://user:password@host:port/dbname[?key=value&key=value...]

使用特殊的 asyncio 中介层,asyncmy 方言可作为 SQLAlchemy asyncio 扩展包的后端使用。

此方言通常只应与create_async_engine()引擎创建函数一起使用:

from sqlalchemy.ext.asyncio import create_async_engine
engine = create_async_engine("mysql+asyncmy://user:pass@hostname/dbname?charset=utf8mb4")

DBAPI

异步 MySQL 的文档和下载信息(如果适用)可在此处获取:github.com/long2ice/asyncmy

连接

连接字符串:

mysql+asyncmy://user:password@host:port/dbname[?key=value&key=value...]

aiomysql

通过 aiomysql 驱动程序支持 MySQL/MariaDB 数据库。

DBAPI

aiomysql 的文档和下载信息(如果适用)可在此处获取:github.com/aio-libs/aiomysql

连接

连接字符串:

mysql+aiomysql://user:password@host:port/dbname[?key=value&key=value...]

aiomysql 方言是 SQLAlchemy 的第二个 Python asyncio 方言。

使用特殊的 asyncio 中介层,aiomysql 方言可作为 SQLAlchemy asyncio 扩展包的后端使用。

此方言通常只应与create_async_engine()引擎创建函数一起使用:

from sqlalchemy.ext.asyncio import create_async_engine
engine = create_async_engine("mysql+aiomysql://user:pass@hostname/dbname?charset=utf8mb4")

DBAPI

aiomysql 的文档和下载信息(如果适用)可在此处获取:github.com/aio-libs/aiomysql

连接

连接字符串:

mysql+aiomysql://user:password@host:port/dbname[?key=value&key=value...]

cymysql

通过 CyMySQL 驱动程序支持 MySQL/MariaDB 数据库。

DBAPI

CyMySQL 的文档和下载信息(如果适用)可在此处获取:github.com/nakagami/CyMySQL

连接

连接字符串:

mysql+cymysql://<username>:<password>@<host>/<dbname>[?<options>]

注意

CyMySQL 方言不在 SQLAlchemy 的持续集成测试范围内,可能存在未解决的问题。推荐使用的 MySQL 方言是 mysqlclient 和 PyMySQL。

DBAPI

CyMySQL 的文档和下载信息(如果适用)可在此处找到:github.com/nakagami/CyMySQL

连接

连接字符串:

mysql+cymysql://<username>:<password>@<host>/<dbname>[?<options>]

pyodbc

通过 PyODBC 驱动程序支持 MySQL / MariaDB 数据库。

DBAPI

PyODBC 的文档和下载信息(如果适用)可在此处找到:pypi.org/project/pyodbc/

连接

连接字符串:

mysql+pyodbc://<username>:<password>@<dsnname>

注意

PyODBC 对于 MySQL 方言不在 SQLAlchemy 的持续集成测试范围内。推荐使用的 MySQL 方言是 mysqlclient 和 PyMySQL。但是,如果您想使用 mysql+pyodbc 方言并且需要对utf8mb4字符(包括表情符号等辅助字符)进行完全支持,请确保使用当前版本的 MySQL Connector/ODBC 并在 DSN 或连接字符串中指定“ANSI”(而不是“Unicode”)版本的驱动程序。

通过精确的 pyodbc 连接字符串进行传递:

import urllib
connection_string = (
    'DRIVER=MySQL ODBC 8.0 ANSI Driver;'
    'SERVER=localhost;'
    'PORT=3307;'
    'DATABASE=mydb;'
    'UID=root;'
    'PWD=(whatever);'
    'charset=utf8mb4;'
)
params = urllib.parse.quote_plus(connection_string)
connection_uri = "mysql+pyodbc:///?odbc_connect=%s" % params

DBAPI

PyODBC 的文档和下载信息(如果适用)可在此处找到:pypi.org/project/pyodbc/

连接

连接字符串:

mysql+pyodbc://<username>:<password>@<dsnname>

SQLite

原文:docs.sqlalchemy.org/en/20/dialects/sqlite.html

对 SQLite 数据库的支持。

以下表格总结了数据库发布版本的当前支持水平。

支持的 SQLite 版本

支持类型 版本
CI 中完全测试过 3.36.0
普通支持 3.12+
尽力而为 3.7.16+

DBAPI 支持

可用以下方言/DBAPI 选项。有关连接信息,请参阅各个 DBAPI 部分。

  • pysqlite

  • aiosqlite

  • pysqlcipher

日期和时间类型

SQLite 没有内置的 DATE、TIME 或 DATETIME 类型,而 pysqlite 也没有提供将值在 Python datetime 对象和 SQLite 支持的格式之间转换的开箱即用功能。当使用 SQLite 时,SQLAlchemy 自己的 DateTime 和相关类型提供日期格式化和解析功能。实现类是 DATETIMEDATETIME。这些类型将日期和时间表示为 ISO 格式的字符串,也很好地支持排序。对于这些函数,不依赖于典型的“libc”内部,因此完全支持历史日期。

确保文本亲和性

这些类型的 DDL 渲染是标准的 DATETIMEDATETIME 指示符。然而,这些类型也可以应用自定义存储格式。当检测到存储格式不包含字母字符时,这些类型的 DDL 被渲染为 DATE_CHARTIME_CHARDATETIME_CHAR,以便列继续具有文本亲和性。

另请参阅

类型亲和性 - SQLite 文档中的说明 ## SQLite 自增行为

SQLite 的自动增量背景资料位于:sqlite.org/autoinc.html

关键概念:

  • SQLite 对于任何非复合主键列都有一个隐式的“自动增量”功能,只要使用“INTEGER PRIMARY KEY”类型 + 主键明确创建该列即可。

  • SQLite 还有一个显式的“AUTOINCREMENT”关键字,它与隐式自增功能等同;不推荐一般使用此关键字。除非使用了特殊的 SQLite 特定指令(见下文),否则 SQLAlchemy 不会渲染此关键字。但仍然要求列的类型命名为“INTEGER”。

使用 AUTOINCREMENT 关键字

要在渲染 DDL 时特别呈现主键列上的 AUTOINCREMENT 关键字,将标志sqlite_autoincrement=True添加到 Table 构造中:

Table('sometable', metadata,
        Column('id', Integer, primary_key=True),
        sqlite_autoincrement=True)

允许自动增量行为的 SQLAlchemy 类型不仅限于 Integer/INTEGER

SQLite 的类型模型基于命名约定。除其他外,这意味着任何包含子字符串"INT"的类型名称将被确定为“整数亲和性”。一个名为"BIGINT""SPECIAL_INT"甚至"XYZINTQPR"的类型,SQLite 都会认为是“整数”亲和性。然而,SQLite 的自动增量功能,无论是隐式还是显式启用,都要求列类型的名称正好是字符串"INTEGER"。因此,如果应用程序对主键使用类似BigInteger的类型,在 SQLite 中,当发出初始CREATE TABLE语句时,此类型需要呈现为名称"INTEGER",以便使自动增量行为可用。

实现此目的的一种方法是仅在 SQLite 上使用Integer,并使用TypeEngine.with_variant()

table = Table(
    "my_table", metadata,
    Column("id", BigInteger().with_variant(Integer, "sqlite"), primary_key=True)
)

另一种方法是使用BigInteger的子类,在针对 SQLite 编译时覆盖其 DDL 名称为INTEGER

from sqlalchemy import BigInteger
from sqlalchemy.ext.compiler import compiles

class SLBigInteger(BigInteger):
    pass

@compiles(SLBigInteger, 'sqlite')
def bi_c(element, compiler, **kw):
    return "INTEGER"

@compiles(SLBigInteger)
def bi_c(element, compiler, **kw):
    return compiler.visit_BIGINT(element, **kw)

table = Table(
    "my_table", metadata,
    Column("id", SLBigInteger(), primary_key=True)
)

另请参阅

TypeEngine.with_variant()

自定义 SQL 构造和编译扩展

SQLite 版本 3 中的数据类型 ## 数据库锁定行为 / 并发性

SQLite 不适用于高并发写入。数据库本身作为文件,在事务中的写操作期间完全被锁定,这意味着在此期间仅有一个“连接”(实际上是一个文件句柄)对数据库具有独占访问权限 - 在此期间所有其他“连接”将被阻塞。

Python DBAPI 规范还要求连接模型始终处于事务中;没有connection.begin()方法,只有connection.commit()connection.rollback(),在其上立即开始新事务。这似乎意味着 SQLite 驱动理论上只允许在任何时候对特定数据库文件进行单个文件句柄的操作;然而,SQLite 本身以及 pysqlite 驱动中有几个因素显著放宽了这一限制。

但是,无论使用何种锁定模式,一旦启动事务并且至少发出了 DML(例如 INSERT、UPDATE、DELETE),SQLite 将始终锁定数据库文件,并且这将至少在其他事务试图发出 DML 时阻止其他事务。默认情况下,此阻塞的时间非常短,然后会超时并显示错误。

当与 SQLAlchemy ORM 结合使用时,此行为变得更加关键。SQLAlchemy 的 Session 对象默认在事务中运行,并且使用其自动刷新模式,可能会在任何 SELECT 语句之前发出 DML。这可能会导致 SQLite 数据库比预期更快地锁定。可以在某种程度上操纵 SQLite 和 pysqlite 驱动程序的锁定模式,但应注意,要在 SQLite 中实现高度的写并发是一场失败的战斗。

有关 SQLite 按设计缺乏写并发的更多信息,请参阅页面底部的 在其他关系数据库管理系统可能更适合的情况下 - 高并发

以下各小节介绍了受 SQLite 文件型架构影响的区域,并在使用 pysqlite 驱动程序时通常需要解决方法才能正常工作。## 事务隔离级别 / 自动提交

SQLite 以非标准方式支持“事务隔离”,沿着两个轴。一个是 PRAGMA read_uncommitted 指令。此设置可以在 SQLite 的默认模式 SERIALIZABLE 隔离和通常称为 READ UNCOMMITTED 的 “脏读” 隔离模式之间切换。

SQLAlchemy 使用 create_engine.isolation_level 参数的 PRAGMA 语句绑定到此。当与 SQLite 结合使用时,此参数的有效值是 "SERIALIZABLE""READ UNCOMMITTED",分别对应值 0 和 1。SQLite 默认为 SERIALIZABLE,但其行为受 pysqlite 驱动程序的默认行为影响。

当使用 pysqlite 驱动程序时,还可以使用 "AUTOCOMMIT" 隔离级别,这将通过 DBAPI 连接上的 .isolation_level 属性来更改 pysqlite 连接,并在设置的持续时间内将其设置为 None。

新版本 1.3.16 中:在使用 pysqlite / sqlite3 SQLite 驱动程序时添加了对 SQLite AUTOCOMMIT 隔离级别的支持。

影响 SQLite 事务性锁定的另一个轴是使用的 BEGIN 语句的性质。三种变体是“deferred”、“immediate” 和 “exclusive”,如 BEGIN TRANSACTION 中所述。直接的 BEGIN 语句使用“deferred”模式,在第一次读取或写入操作之前不会锁定数据库文件,并且在第一次写入操作之前会保持对其他事务的读取访问打开。但是,关键要注意的是 pysqlite 驱动程序通过甚至不发出 BEGIN来干扰此行为。

警告

SQLite 的事务范围受到 pysqlite 驱动程序中未解决的问题的影响,该驱动程序将 BEGIN 语句推迟到比通常更大的程度。请参阅 Serializable isolation / Savepoints / Transactional DDL 或 Serializable isolation / Savepoints / Transactional DDL (asyncio version) 部分,了解解决此行为的技术。

另请参见

设置事务隔离级别,包括 DBAPI 自动提交

INSERT/UPDATE/DELETE…RETURNING

SQLite 方言支持 SQLite 3.35 的 INSERT|UPDATE|DELETE..RETURNING 语法。在某些情况下,INSERT..RETURNING 可以自动使用,以在生成新标识符时替代传统方法使用 cursor.lastrowid,但是在简单的单语句情况下,目前仍更倾向于使用 cursor.lastrowid,因为其性能更好。

要指定显式的 RETURNING 子句,请在每个语句基础上使用 _UpdateBase.returning() 方法:

# INSERT..RETURNING
result = connection.execute(
    table.insert().
    values(name='foo').
    returning(table.c.col1, table.c.col2)
)
print(result.all())

# UPDATE..RETURNING
result = connection.execute(
    table.update().
    where(table.c.name=='foo').
    values(name='bar').
    returning(table.c.col1, table.c.col2)
)
print(result.all())

# DELETE..RETURNING
result = connection.execute(
    table.delete().
    where(table.c.name=='foo').
    returning(table.c.col1, table.c.col2)
)
print(result.all())

版本 2.0 中的新功能:添加对 SQLite RETURNING 的支持

SAVEPOINT 支持

SQLite 支持 SAVEPOINT,仅在事务开始后才起作用。SQLAlchemy 的 SAVEPOINT 支持可使用 Core 级别的 Connection.begin_nested() 方法和 ORM 级别的 Session.begin_nested() 方法。但是,除非采取解决方法,否则在 pysqlite 中根本无法使用 SAVEPOINT。

警告

SQLite 的 SAVEPOINT 功能受到 pysqlite 和 aiosqlite 驱动程序中未解决的问题的影响,这些驱动程序将 BEGIN 语句推迟到比通常更大的程度。请参阅 Serializable isolation / Savepoints / Transactional DDL 和 Serializable isolation / Savepoints / Transactional DDL (asyncio version) 部分,了解解决此行为的技术。

事务性 DDL

SQLite 数据库也支持事务性 DDL。在这种情况下,pysqlite 驱动程序不仅未能启动事务,还在检测到 DDL 时结束了任何现有事务,因此需要解决方法。

警告

SQLite 的事务 DDL 受到 pysqlite 驱动程序中未解决的问题的影响,该驱动程序在遇到 DDL 时未发出 BEGIN 并且还强制执行 COMMIT 以取消任何事务。请参阅 Serializable isolation / Savepoints / Transactional DDL 部分以了解解决此行为的技巧。

外键支持

SQLite 在发出 CREATE 语句创建表时支持 FOREIGN KEY 语法,但默认情况下这些约束对表的操作没有任何影响。

在 SQLite 上进行约束检查有三个前提条件:

  • 必须使用至少版本 3.6.19 的 SQLite。

  • SQLite 库必须编译为 不包含 SQLITE_OMIT_FOREIGN_KEY 或 SQLITE_OMIT_TRIGGER 符号的状态。

  • 必须在所有连接上发出 PRAGMA foreign_keys = ON 语句,包括对 MetaData.create_all() 的初始调用。

SQLAlchemy 允许通过事件的使用自动发出 PRAGMA 语句以用于新连接:

from sqlalchemy.engine import Engine
from sqlalchemy import event

@event.listens_for(Engine, "connect")
def set_sqlite_pragma(dbapi_connection, connection_record):
    cursor = dbapi_connection.cursor()
    cursor.execute("PRAGMA foreign_keys=ON")
    cursor.close()

警告

当启用 SQLite 外键时,不可能 发出包含相互依赖外键约束的表的 CREATE 或 DROP 语句;要为这些表发出 DDL,需要使用 ALTER TABLE 分别创建或删除这些约束,而 SQLite 不支持此操作。

另请参阅

SQLite 外键支持 - SQLite 网站上的链接。

Events - SQLAlchemy 事件 API。

通过 ALTER 创建/删除外键约束 - 关于 SQLAlchemy 处理的更多信息

相互依赖的外键约束。## 用于约束的 ON CONFLICT 支持

另请参阅

本节描述了 SQLite 中在 CREATE TABLE 语句内部发生的 “ON CONFLICT” 的 DDL 版本。有关作用于 INSERT 语句的 “ON CONFLICT”,请参阅 INSERT…ON CONFLICT (Upsert)。

SQLite 支持一个名为 ON CONFLICT 的非标准 DDL 子句,可应用于主键、唯一、检查和非空约束。在 DDL 中,它要么在“CONSTRAINT”子句中呈现,要么在目标约束的位置取决于列定义本身。要在 DDL 中呈现此子句,可以使用扩展参数sqlite_on_conflict并在PrimaryKeyConstraintUniqueConstraintCheckConstraint对象中指定字符串冲突解析算法。在Column对象中,有单独的参数sqlite_on_conflict_not_nullsqlite_on_conflict_primary_keysqlite_on_conflict_unique,它们分别对应于可以从Column对象指示的三种相关约束类型。

另请参见

ON CONFLICT - SQLite 文档中的内容

版本 1.3 中的新功能。

sqlite_on_conflict参数接受一个字符串参数,该参数只是要选择的解析名称,在 SQLite 中可以是 ROLLBACK、ABORT、FAIL、IGNORE 和 REPLACE 中的一个。例如,要添加指定 IGNORE 算法的唯一约束:

some_table = Table(
    'some_table', metadata,
    Column('id', Integer, primary_key=True),
    Column('data', Integer),
    UniqueConstraint('id', 'data', sqlite_on_conflict='IGNORE')
)

以上呈现了 CREATE TABLE DDL 如下:

CREATE TABLE some_table (
    id INTEGER NOT NULL,
    data INTEGER,
    PRIMARY KEY (id),
    UNIQUE (id, data) ON CONFLICT IGNORE
)

当使用Column.unique标志将唯一约束添加到单个列时,也可以将sqlite_on_conflict_unique参数添加到Column中,该参数将添加到 DDL 中的唯一约束中:

some_table = Table(
    'some_table', metadata,
    Column('id', Integer, primary_key=True),
    Column('data', Integer, unique=True,
           sqlite_on_conflict_unique='IGNORE')
)

渲染:

CREATE TABLE some_table (
    id INTEGER NOT NULL,
    data INTEGER,
    PRIMARY KEY (id),
    UNIQUE (data) ON CONFLICT IGNORE
)

要应用 FAIL 算法以满足非空约束,使用sqlite_on_conflict_not_null

some_table = Table(
    'some_table', metadata,
    Column('id', Integer, primary_key=True),
    Column('data', Integer, nullable=False,
           sqlite_on_conflict_not_null='FAIL')
)

这将呈现列内联的 ON CONFLICT 短语:

CREATE TABLE some_table (
    id INTEGER NOT NULL,
    data INTEGER NOT NULL ON CONFLICT FAIL,
    PRIMARY KEY (id)
)

类似地,对于内联主键,请使用sqlite_on_conflict_primary_key

some_table = Table(
    'some_table', metadata,
    Column('id', Integer, primary_key=True,
           sqlite_on_conflict_primary_key='FAIL')
)

SQLAlchemy 单独呈现主键约束,因此冲突解析算法应用于约束本身:

CREATE TABLE some_table (
    id INTEGER NOT NULL,
    PRIMARY KEY (id) ON CONFLICT FAIL
)
```  ## INSERT…ON CONFLICT(Upsert)

另请参见

本节描述了 SQLite 中“ON CONFLICT”的 DML 版本,它出现在 INSERT 语句中。有关应用于 CREATE TABLE 语句的“ON CONFLICT”,请参见 ON CONFLICT 支持约束。

从版本 3.24.0 开始,SQLite 支持通过 `INSERT` 语句的 `ON CONFLICT` 子句将行“upsert”(更新或插入)到表中。只有候选行不违反任何唯一约束或主键约束时,才会插入候选行。在唯一约束违反的情况下,可以发生二次操作,可以是“DO UPDATE”,表示目标行中的数据应该更新,也可以是“DO NOTHING”,表示要默默跳过此行。

冲突是使用现有唯一约束和索引的列确定的。这些约束通过说明组成索引的列和条件来识别。

SQLAlchemy 通过 SQLite 特定的 `insert()` 函数提供了 `ON CONFLICT` 支持,该函数提供了生成方法 `Insert.on_conflict_do_update()` 和 `Insert.on_conflict_do_nothing()`:

```py
>>> from sqlalchemy.dialects.sqlite import insert

>>> insert_stmt = insert(my_table).values(
...     id='some_existing_id',
...     data='inserted value')

>>> do_update_stmt = insert_stmt.on_conflict_do_update(
...     index_elements=['id'],
...     set_=dict(data='updated value')
... )

>>> print(do_update_stmt)
INSERT  INTO  my_table  (id,  data)  VALUES  (?,  ?)
ON  CONFLICT  (id)  DO  UPDATE  SET  data  =  ?
>>> do_nothing_stmt = insert_stmt.on_conflict_do_nothing(
...     index_elements=['id']
... )

>>> print(do_nothing_stmt)
INSERT  INTO  my_table  (id,  data)  VALUES  (?,  ?)
ON  CONFLICT  (id)  DO  NOTHING 

版本 1.4 中的新功能。

另请参阅

Upsert - SQLite 文档中的内容。

指定目标

这两种方法都使用列推断提供冲突的“目标”:

  • Insert.on_conflict_do_update.index_elements 参数指定一个序列,其中包含字符串列名、Column 对象和/或 SQL 表达式元素,用于标识唯一索引或唯一约束。

  • 当使用 Insert.on_conflict_do_update.index_elements 推断索引时,也可以通过指定 Insert.on_conflict_do_update.index_where 参数来推断部分索引:

    >>> stmt = insert(my_table).values(user_email='a@b.com', data='inserted data')
    
    >>> do_update_stmt = stmt.on_conflict_do_update(
    ...     index_elements=[my_table.c.user_email],
    ...     index_where=my_table.c.user_email.like('%@gmail.com'),
    ...     set_=dict(data=stmt.excluded.data)
    ...     )
    
    >>> print(do_update_stmt)
    INSERT  INTO  my_table  (data,  user_email)  VALUES  (?,  ?)
    ON  CONFLICT  (user_email)
    WHERE  user_email  LIKE  '%@gmail.com'
    DO  UPDATE  SET  data  =  excluded.data 
    

SET 子句

ON CONFLICT...DO UPDATE 用于执行已存在行的更新操作,使用新值以及建议插入的值的任意组合。这些值使用 Insert.on_conflict_do_update.set_ 参数指定。该参数接受一个字典,其中包含更新的直接值:

>>> stmt = insert(my_table).values(id='some_id', data='inserted value')

>>> do_update_stmt = stmt.on_conflict_do_update(
...     index_elements=['id'],
...     set_=dict(data='updated value')
... )

>>> print(do_update_stmt)
INSERT  INTO  my_table  (id,  data)  VALUES  (?,  ?)
ON  CONFLICT  (id)  DO  UPDATE  SET  data  =  ? 

警告

Insert.on_conflict_do_update() 方法不会考虑 Python 端的默认 UPDATE 值或生成函数,例如使用 Column.onupdate 指定的值。这些值不会在 ON CONFLICT 类型的 UPDATE 中执行,除非它们在 Insert.on_conflict_do_update.set_ 字典中手动指定。

使用排除的 INSERT 值进行更新

要引用提议的插入行,Insert.excluded 这个特殊别名可作为 Insert 对象的属性使用;这个对象在列上创建一个“excluded.” 前缀,通知 DO UPDATE 使用将要插入的值来更新行,如果约束没有失败的话:

>>> stmt = insert(my_table).values(
...     id='some_id',
...     data='inserted value',
...     author='jlh'
... )

>>> do_update_stmt = stmt.on_conflict_do_update(
...     index_elements=['id'],
...     set_=dict(data='updated value', author=stmt.excluded.author)
... )

>>> print(do_update_stmt)
INSERT  INTO  my_table  (id,  data,  author)  VALUES  (?,  ?,  ?)
ON  CONFLICT  (id)  DO  UPDATE  SET  data  =  ?,  author  =  excluded.author 

额外的 WHERE 条件

Insert.on_conflict_do_update() 方法还接受使用 Insert.on_conflict_do_update.where 参数的 WHERE 子句,这将限制接收 UPDATE 的行:

>>> stmt = insert(my_table).values(
...     id='some_id',
...     data='inserted value',
...     author='jlh'
... )

>>> on_update_stmt = stmt.on_conflict_do_update(
...     index_elements=['id'],
...     set_=dict(data='updated value', author=stmt.excluded.author),
...     where=(my_table.c.status == 2)
... )
>>> print(on_update_stmt)
INSERT  INTO  my_table  (id,  data,  author)  VALUES  (?,  ?,  ?)
ON  CONFLICT  (id)  DO  UPDATE  SET  data  =  ?,  author  =  excluded.author
WHERE  my_table.status  =  ? 

使用 DO NOTHING 跳过行

ON CONFLICT 可用于完全跳过插入行,如果与唯一约束发生冲突;下面使用 Insert.on_conflict_do_nothing() 方法进行说明:

>>> stmt = insert(my_table).values(id='some_id', data='inserted value')
>>> stmt = stmt.on_conflict_do_nothing(index_elements=['id'])
>>> print(stmt)
INSERT  INTO  my_table  (id,  data)  VALUES  (?,  ?)  ON  CONFLICT  (id)  DO  NOTHING 

如果使用 DO NOTHING 而没有指定任何列或约束,它将跳过任何唯一性冲突导致的 INSERT:

>>> stmt = insert(my_table).values(id='some_id', data='inserted value')
>>> stmt = stmt.on_conflict_do_nothing()
>>> print(stmt)
INSERT  INTO  my_table  (id,  data)  VALUES  (?,  ?)  ON  CONFLICT  DO  NOTHING 
```  ## 类型反射

SQLite 类型与大多数其他数据库后端的类型不同,因为类型的字符串名称通常不是一对一对应的“类型”。相反,SQLite 将每列的类型行为链接到五种所谓的“类型亲和性”之一,基于类型的字符串匹配模式。

SQLAlchemy 的反射过程,在检查类型时,使用一个简单的查找表将返回的关键字链接到提供的 SQLAlchemy 类型。这个查找表存在于 SQLite 方言中,就像所有其他方言一样。然而,当特定类型名称未在查找映射中找到时,SQLite 方言有一个不同的“回退”程序;它实际上实现了位于 [`www.sqlite.org/datatype3.html`](https://www.sqlite.org/datatype3.html) 第 2.1 节的 SQLite “类型亲和性”方案。

提供的类型映射将直接从以下类型的精确字符串名称匹配中进行关联:

如果类型名称包含字符串`BLOB`,则返回`BIGINT`、`BLOB`、`BOOLEAN`、`BOOLEAN`、`CHAR`、`DATE`、`DATETIME`、`FLOAT`、`DECIMAL`、`FLOAT`、`INTEGER`、`INTEGER`、`NUMERIC`、`REAL`、`SMALLINT`、`TEXT`、`TIME`、`TIMESTAMP`、`VARCHAR`、`NVARCHAR`、`NCHAR`

当类型名称不匹配上述类型之一时,将使用“类型亲和性”查找代替:

+   如果类型名称包含字符串`INT`,则返回`INTEGER`

+   如果类型名称包含字符串`CHAR`、`CLOB`或`TEXT`,则返回`TEXT`

+   如果类型名称包含字符串`BLOB`,则返回`NullType`

+   如果类型名称包含字符串`REAL`、`FLOA`或`DOUB`,则返回`REAL`

+   否则,将使用`NUMERIC`类型。## 部分索引

可以使用 DDL 系统使用参数`sqlite_where`来指定部分索引,例如使用 WHERE 子句的索引:

```py
tbl = Table('testtbl', m, Column('data', Integer))
idx = Index('test_idx1', tbl.c.data,
            sqlite_where=and_(tbl.c.data > 5, tbl.c.data < 10))

索引将在创建时呈现为:

CREATE INDEX test_idx1 ON testtbl (data)
WHERE data > 5 AND data < 10
```  ## 点列名

使用明确包含句点的表格或列名**不推荐**。虽然这通常对关系数据库来说是个坏主意,因为句点是一个语法上重要的字符,但直到 SQLite 版本**3.10.0**之前的 SQLite 驱动程序存在一个 bug,需要 SQLAlchemy 在结果集中过滤掉这些句点。

这个 bug 完全不是 SQLAlchemy 的问题,可以这样说明:

```py
import sqlite3

assert sqlite3.sqlite_version_info < (3, 10, 0), "bug is fixed in this version"

conn = sqlite3.connect(":memory:")
cursor = conn.cursor()

cursor.execute("create table x (a integer, b integer)")
cursor.execute("insert into x (a, b) values (1, 1)")
cursor.execute("insert into x (a, b) values (2, 2)")

cursor.execute("select x.a, x.b from x")
assert [c[0] for c in cursor.description] == ['a', 'b']

cursor.execute('''
 select x.a, x.b from x where a=1
 union
 select x.a, x.b from x where a=2
''')
assert [c[0] for c in cursor.description] == ['a', 'b'], \
    [c[0] for c in cursor.description]

第二个断言失败:

Traceback (most recent call last):
  File "test.py", line 19, in <module>
    [c[0] for c in cursor.description]
AssertionError: ['x.a', 'x.b']

在上述情况下,驱动程序错误地报告包括表名在内的列名,这与没有 UNION 时完全不一致。

SQLAlchemy 依赖于列名在匹配原始语句时的可预测性,因此 SQLAlchemy 方言别无选择,只能过滤掉这些内容:

from sqlalchemy import create_engine

eng = create_engine("sqlite://")
conn = eng.connect()

conn.exec_driver_sql("create table x (a integer, b integer)")
conn.exec_driver_sql("insert into x (a, b) values (1, 1)")
conn.exec_driver_sql("insert into x (a, b) values (2, 2)")

result = conn.exec_driver_sql("select x.a, x.b from x")
assert result.keys() == ["a", "b"]

result = conn.exec_driver_sql('''
 select x.a, x.b from x where a=1
 union
 select x.a, x.b from x where a=2
''')
assert result.keys() == ["a", "b"]

请注意,即使 SQLAlchemy 过滤掉了句点,这两个名称仍然可寻址

>>> row = result.first()
>>> row["a"]
1
>>> row["x.a"]
1
>>> row["b"]
1
>>> row["x.b"]
1

因此,SQLAlchemy 应用的解决方法仅影响公共 API 中的CursorResult.keys()Row.keys(),在应用被迫使用包含句点的列名,并且需要CursorResult.keys()Row.keys()返回这些带点的名称时,可以提供sqlite_raw_colnames执行选项,或者基于每个Connection的基础上:

result = conn.execution_options(sqlite_raw_colnames=True).exec_driver_sql('''
 select x.a, x.b from x where a=1
 union
 select x.a, x.b from x where a=2
''')
assert result.keys() == ["x.a", "x.b"]

或者基于每个Engine的基础上:

engine = create_engine("sqlite://", execution_options={"sqlite_raw_colnames": True})

在使用基于每个Engine的执行选项时,请注意使用 UNION 的 Core 和 ORM 查询可能无法正常工作

特定于 SQLite 的表选项

一个 CREATE TABLE 的选项直接由 SQLite 方言支持,与Table构造一起使用:

  • WITHOUT ROWID

    Table("some_table", metadata, ..., sqlite_with_rowid=False)
    

另请参见

SQLite CREATE TABLE options

反射内部模式表

返回表列表的反射方法将省略所谓的“SQLite 内部模式对象”名称,这些名称被 SQLite 视为任何以sqlite_为前缀的对象名称。这种对象的一个例子是在使用AUTOINCREMENT列参数时生成的sqlite_sequence表。为了返回这些对象,可以将参数sqlite_include_internal=True传递给诸如MetaData.reflect()Inspector.get_table_names()等方法。

新增于版本 2.0:添加了 sqlite_include_internal=True 参数。以前,这些表不会被 SQLAlchemy 反射方法所忽略。

sqlite_include_internal 参数不是指与 sqlite_master 等模式中存在的“系统”表相关的内容。

另请参见

SQLite 内部模式对象 - 在 SQLite 文档中。

SQLite 数据类型

与所有 SQLAlchemy 方言一样,所有已知与 SQLite 兼容的大写类型都可以从顶级方言导入,无论它们是来自 sqlalchemy.types 还是本地方言:

from sqlalchemy.dialects.sqlite import (
    BLOB,
    BOOLEAN,
    CHAR,
    DATE,
    DATETIME,
    DECIMAL,
    FLOAT,
    INTEGER,
    NUMERIC,
    JSON,
    SMALLINT,
    TEXT,
    TIME,
    TIMESTAMP,
    VARCHAR,
)
对象名称 描述
日期 使用字符串在 SQLite 中表示 Python 日期对象。
日期时间 使用字符串在 SQLite 中表示 Python 日期时间对象。
JSON SQLite JSON 类型。
时间 使用字符串在 SQLite 中表示 Python 时间对象。
class sqlalchemy.dialects.sqlite.DATETIME

使用字符串在 SQLite 中表示 Python 日期时间对象。

默认的字符串存储格式为:

"%(year)04d-%(month)02d-%(day)02d  %(hour)02d:%(minute)02d:%(second)02d.%(microsecond)06d"

例如:

2021-03-15 12:05:57.105542

默认情况下,传入的存储格式将使用 Python 的 datetime.fromisoformat() 函数解析。

从版本 2.0 开始更改:默认日期时间字符串解析使用 datetime.fromisoformat()

可以使用 storage_formatregexp 参数在一定程度上定制存储格式,例如:

import re
from sqlalchemy.dialects.sqlite import DATETIME

dt = DATETIME(storage_format="%(year)04d/%(month)02d/%(day)02d "
                             "%(hour)02d:%(minute)02d:%(second)02d",
              regexp=r"(\d+)/(\d+)/(\d+) (\d+)-(\d+)-(\d+)"
)

参数:

  • storage_format – 格式字符串,将应用于具有键年、月、日、小时、分钟、秒和微秒的字典。

  • regexp – 将应用于传入结果行的正则表达式,替换使用 datetime.fromisoformat() 解析传入字符串的方法。如果正则表达式包含命名组,则将生成的匹配字典作为关键字参数应用于 Python 的 datetime() 构造函数。否则,如果使用了位置组,则通过 *map(int, match_obj.groups(0)) 调用 datetime() 构造函数以使用位置参数。

类签名

sqlalchemy.dialects.sqlite.DATETIMEsqlalchemy.dialects.sqlite.base._DateTimeMixinsqlalchemy.types.DateTime)

class sqlalchemy.dialects.sqlite.DATE

使用字符串在 SQLite 中表示 Python 日期对象。

默认的字符串存储格式为:

"%(year)04d-%(month)02d-%(day)02d"

例如:

2011-03-15

默认情况下,传入的存储格式将使用 Python 的 date.fromisoformat() 函数解析。

从版本 2.0 开始更改:默认日期字符串解析使用 date.fromisoformat()

可以使用 storage_formatregexp 参数在一定程度上定制存储格式,例如:

import re
from sqlalchemy.dialects.sqlite import DATE

d = DATE(
        storage_format="%(month)02d/%(day)02d/%(year)04d",
        regexp=re.compile("(?P<month>\d+)/(?P<day>\d+)/(?P<year>\d+)")
    )

参数:

  • storage_format – 格式字符串,将应用于具有键年、月和日的字典。

  • regexp – 将应用于传入结果行的正则表达式,以替换使用 date.fromisoformat() 来解析传入字符串。如果正则表达式包含命名组,则生成的匹配字典将作为关键字参数应用于 Python 的 date() 构造函数。否则,如果使用位置组,则通过 *map(int, match_obj.groups(0)) 调用 date() 构造函数来传递位置参数。

类签名

class sqlalchemy.dialects.sqlite.DATE (sqlalchemy.dialects.sqlite.base._DateTimeMixin, sqlalchemy.types.Date)

class sqlalchemy.dialects.sqlite.JSON

SQLite JSON 类型。

SQLite 从版本 3.9 开始支持 JSON,通过其 JSON1 扩展。请注意,JSON1 是一个可加载扩展,因此可能不可用,或者可能需要运行时加载。

在 SQLite 后端中使用基本 JSON 数据类型时,JSON 会自动使用。

另请参阅

JSON - 通用跨平台 JSON 数据类型的主文档。

JSON 类型支持将 JSON 值持久化,以及通过在数据库级别包装 JSON_EXTRACT 函数并渲染为 JSON_QUOTE 函数来提供核心索引操作的 JSON 数据类型,以适应这些操作。提取的值都被引用,以确保结果始终是 JSON 字符串值。

版本 1.3 中的新功能。

成员

init()

类签名

class sqlalchemy.dialects.sqlite.JSON (sqlalchemy.types.JSON)

method __init__(none_as_null: bool = False)

继承自 JSONsqlalchemy.types.JSON.__init__ 方法

构造一个 JSON 类型。

参数:

none_as_null=False

如果为 True,则将值 None 持久化为 SQL NULL 值,而不是 null 的 JSON 编码。注意,当此标志为 False 时,仍然可以使用 null() 构造来持久化 NULL 值,该值可以直接作为参数值传递,由 JSON 类型特殊解释为 SQL NULL:

from sqlalchemy import null
conn.execute(table.insert(), {"data": null()})

注意

JSON.none_as_null 适用于传递给 Column.defaultColumn.server_default 的值;这些参数的传递值为 None 意味着“没有默认值”。

此外,在 SQL 比较表达式中使用时,Python 值 None 仍然表示 SQL 空值,而不是 JSON NULL。JSON.none_as_null 标志显式指定了值在 INSERT 或 UPDATE 语句中的持久性。应该使用 JSON.NULL 值来表示希望与 JSON 空值进行比较的 SQL 表达式。

另请参阅

JSON.NULL

class sqlalchemy.dialects.sqlite.TIME

使用字符串在 SQLite 中表示 Python 时间对象。

默认字符串存储格式为:

"%(hour)02d:%(minute)02d:%(second)02d.%(microsecond)06d"

例如:

12:05:57.10558

默认情况下,传入的存储格式使用 Python 的 time.fromisoformat() 函数解析。

自 2.0 版本更改:默认时间字符串解析现在使用 time.fromisoformat()

存储格式可以在一定程度上使用 storage_formatregexp 参数进行自定义,例如:

import re
from sqlalchemy.dialects.sqlite import TIME

t = TIME(storage_format="%(hour)02d-%(minute)02d-"
                        "%(second)02d-%(microsecond)06d",
         regexp=re.compile("(\d+)-(\d+)-(\d+)-(?:-(\d+))?")
)

参数:

  • storage_format – 将应用于包含小时、分钟、秒和微秒键的字典的格式字符串。

  • regexp – 将应用于传入结果行的正则表达式,取代使用 datetime.fromisoformat() 解析传入字符串。如果正则表达式包含命名组,则结果匹配字典将作为关键字参数应用于 Python 的 time() 构造函数。否则,如果使用了位置组,则通过 *map(int, match_obj.groups(0)) 将调用 time() 构造函数以传递位置参数。

类签名

class sqlalchemy.dialects.sqlite.TIME (sqlalchemy.dialects.sqlite.base._DateTimeMixin, sqlalchemy.types.Time)

SQLite DML Constructs

对象名称 描述
insert(table) 构造一个特定于 SQLite 的变体 Insert 构造。
Insert SQLite 的 INSERT 的特定实现。
function sqlalchemy.dialects.sqlite.insert(table: _DMLTableArgument) → Insert

构造一个特定于 SQLite 的变体 Insert 构造。

sqlalchemy.dialects.sqlite.insert() 函数创建 sqlalchemy.dialects.sqlite.Insert。该类基于方言无关的 Insert 结构,可以使用 SQLAlchemy Core 中的 insert() 函数构造。

Insert 结构包括额外的方法 Insert.on_conflict_do_update()Insert.on_conflict_do_nothing()

class sqlalchemy.dialects.sqlite.Insert

SQLite 特定的 INSERT 实现。

添加了针对 SQLite 特定语法的方法,如 ON CONFLICT。

Insert 对象是通过 sqlalchemy.dialects.sqlite.insert() 函数创建的。

在 1.4 版中新增。

另请参阅

INSERT…ON CONFLICT(插入或替换)

成员

excluded、inherit_cache、on_conflict_do_nothing()、on_conflict_do_update()

类签名

class sqlalchemy.dialects.sqlite.Insertsqlalchemy.sql.expression.Insert

attribute excluded

为 ON CONFLICT 语句提供 excluded 命名空间。

SQLite 的 ON CONFLICT 子句允许引用将要插入的行,称为 excluded。此属性提供了对此行中的所有列的引用。

提示

Insert.excluded 属性是 ColumnCollection 的一个实例,它提供与访问表和列描述的 Table.c 集合相同的接口。通过这个集合,普通名称可以像属性一样访问(例如 stmt.excluded.some_column),但特殊名称和字典方法名称应使用索引访问,例如 stmt.excluded["column name"]stmt.excluded["values"]。有关更多示例,请参阅 ColumnCollection 的文档字符串。

attribute inherit_cache: bool | None = False

指示此 HasCacheKey 实例是否应使用其直接超类使用的缓存密钥生成方案。

此属性默认为 None,表示构造尚未考虑是否适合参与缓存;这在功能上相当于将值设置为 False,但还会发出警告。

如果与此类本地属性(而不是其超类)无关,则可以在特定类上设置此标志为 True,则与对象对应的 SQL 不会根据这个类的属性而改变。

另请参阅

为自定义结构启用缓存支持 - 设置HasCacheKey.inherit_cache 属性的通用指南,用于第三方或用户定义的 SQL 结构。

method on_conflict_do_nothing(index_elements: _OnConflictIndexElementsT = None, index_where: _OnConflictIndexWhereT = None) → Self

指定了 ON CONFLICT 子句的 DO NOTHING 操作。

参数:

  • index_elements – 由字符串列名、Column 对象或其他列表达式对象组成的序列,将用于推断目标索引或唯一约束。

  • index_where – 用于推断条件目标索引的额外 WHERE 条件。

method on_conflict_do_update(index_elements: _OnConflictIndexElementsT = None, index_where: _OnConflictIndexWhereT = None, set_: _OnConflictSetT = None, where: _OnConflictWhereT = None) → Self

指定了 ON CONFLICT 子句的 DO UPDATE SET 操作。

参数:

  • index_elements – 由字符串列名、Column 对象或其他列表达式对象组成的序列,将用于推断目标索引或唯一约束。

  • index_where – 用于推断条件目标索引的额外 WHERE 条件。

  • set_

    一个字典或其他映射对象,其中键是目标表中的列名称,或者是 Column 对象或其他 ORM 映射的列,匹配目标表的列,值是表达式或文字,指定要采取的 SET 操作。

    从版本 1.4 开始:Insert.on_conflict_do_update.set_ 参数支持目标 Table 中的 Column 对象作为键。

    警告

    此字典考虑 Python 指定的默认 UPDATE 值或生成函数,例如使用 Column.onupdate 指定的值。除非在 Insert.on_conflict_do_update.set_ 字典中手动指定,否则这些值将不会用于 ON CONFLICT 类型的 UPDATE。

  • where – 可选参数。如果存在,则可以是一个文字 SQL 字符串或一个可接受的 WHERE 子句表达式,用于限制受 DO UPDATE SET 影响的行。不满足 WHERE 条件的行将不会更新(对于这些行实际上是 DO NOTHING)。

Pysqlite

通过 pysqlite 驱动程序支持 SQLite 数据库。

请注意,pysqlite 与 Python 发行版中包含的 sqlite3 模块是相同的驱动程序。

DBAPI

pysqlite 的文档和下载信息(如果适用)可在此处找到:docs.python.org/library/sqlite3.html

连接

连接字符串:

sqlite+pysqlite:///file_path

驱动程序

在所有现代 Python 版本上,sqlite3 Python DBAPI 都是标准的;对于 cPython 和 Pypy,不需要额外安装。

连接字符串

SQLite 数据库的文件规范被视为 URL 的 “数据库” 部分。请注意,SQLAlchemy URL 的格式为:

driver://user:pass@host/database

这意味着要使用的实际文件名从第三个斜杠的右边开始。因此,连接到相对文件路径看起来像:

# relative path
e = create_engine('sqlite:///path/to/database.db')

绝对路径,以斜杠开头表示,意味着您需要四个斜杠:

# absolute path
e = create_engine('sqlite:////path/to/database.db')

要使用 Windows 路径,可以使用常规的驱动器规范和反斜杠。可能需要双反斜杠:

# absolute path on Windows
e = create_engine('sqlite:///C:\\path\\to\\database.db')

要使用 sqlite :memory: 数据库,请将其指定为使用 sqlite://:memory: 的文件名。如果没有文件路径,指定只有 sqlite:// 而没有其他内容:

# in-memory database
e = create_engine('sqlite://:memory:')
# also in-memory database
e2 = create_engine('sqlite://')

URI 连接

现代版本的 SQLite 支持使用驱动级 URI进行连接的另一种系统,其优势在于可以传递附加的驱动级参数,包括诸如“只读”之类的选项。Python 的 sqlite3 驱动在现代 Python 3 版本下支持此模式。SQLAlchemy 的 pysqlite 驱动通过在 URL 查询字符串中指定“uri=true”来支持此使用模式。SQLite 级别的“URI”被保留为 SQLAlchemy URL 的“database”部分(即在斜杠后面):

e = create_engine("sqlite:///file:path/to/database?mode=ro&uri=true")

注意

“uri=true”参数必须出现在 URL 的查询字符串中。如果仅出现在create_engine.connect_args参数字典中,则目前不会按预期工作。

该逻辑通过分离属于 Python sqlite3 驱动程序和属于 SQLite URI 的参数来协调 SQLAlchemy 查询字符串和 SQLite 查询字符串的同时存在。这是通过使用已知被 Python 驱动程序的固定参数列表来实现的。例如,要包含指示 Python sqlite3“timeout”和“check_same_thread”参数以及 SQLite“mode”和“nolock”参数的 URL,它们都可以一起传递到查询字符串中:

e = create_engine(
    "sqlite:///file:path/to/database?"
    "check_same_thread=true&timeout=10&mode=ro&nolock=1&uri=true"
)

如上,pysqlite / sqlite3 DBAPI 将传递参数为:

sqlite3.connect(
    "file:path/to/database?mode=ro&nolock=1",
    check_same_thread=True, timeout=10, uri=True
)

关于将来添加到 Python 或本机驱动程序的参数。 SQLite URI 方案中添加的新参数名称应该会自动适应此方案。可以通过在create_engine.connect_args字典中指定它们来适应 Python 驱动程序端添加的新参数名称,直到 SQLAlchemy 添加了方言支持为止。对于本机 SQLite 驱动程序添加的新参数名称与现有的已知 Python 驱动程序参数之一(例如“timeout”)重叠的不太可能的情况,SQLAlchemy 的方言将需要调整 URL 方案以继续支持此参数。

对于所有 SQLAlchemy 方言,可以通过create_engine()create_engine.creator参数绕过整个“URL”过程,该参数允许创建直接创建 Python sqlite3 驱动级连接的自定义可调用函数。

新版本中新增。

另请参见

统一资源标识符 - SQLite 文档中的正则表达式支持 ### 正则表达式支持

新版本中新增。

使用 Python 的 re.search 函数提供了对 ColumnOperators.regexp_match() 运算符的支持。SQLite 本身不包括可用的正则表达式运算符;相反,它包括一个未实现的占位符运算符 REGEXP,调用必须提供的用户定义函数。

SQLAlchemy 的实现使用 pysqlite 的 create_function 钩子,如下所示:

def regexp(a, b):
    return re.search(a, b) is not None

sqlite_connection.create_function(
    "regexp", 2, regexp,
)

目前不支持将正则表达式标志作为单独参数,因为这些标志不受 SQLite 的 REGEXP 运算符支持,但可以在正则表达式字符串内联包含。详见Python 正则表达式

另请参见

Python 正则表达式:Python 正则表达式语法的文档。

与 sqlite3 “本地”日期和日期时间类型兼容

pysqlite 驱动程序包括 sqlite3.PARSE_DECLTYPES 和 sqlite3.PARSE_COLNAMES 选项,这些选项的效果是任何明确转换为“date”或“timestamp”的列或表达式将转换为 Python 日期或日期时间对象。pysqlite 方言提供的日期和日期时间类型目前与这些选项不兼容,因为它们呈现 ISO 日期/日期时间,包括微秒,而 pysqlite 的驱动程序不包括。此外,SQLAlchemy 目前不会自动呈现“cast”语法,以使自由函数“current_timestamp”和“current_date”返回原生的 datetime/date 类型。不幸的是,pysqlite 不提供 cursor.description 中的标准 DBAPI 类型,使得 SQLAlchemy 无法在不进行昂贵的每行类型检查的情况下动态检测这些类型。

请注意,不推荐使用 pysqlite 的解析选项,也不应该使用 SQLAlchemy,如果配置了 "native_datetime=True" 在 create_engine() 上,可以强制使用 PARSE_DECLTYPES。

engine = create_engine('sqlite://',
    connect_args={'detect_types':
        sqlite3.PARSE_DECLTYPES|sqlite3.PARSE_COLNAMES},
    native_datetime=True
)

启用此标志后,DATE 和 TIMESTAMP 类型(但请注意 - 不是 DATETIME 或 TIME 类型...还困惑吗?)将不执行任何绑定参数或结果处理。执行 “func.current_date()” 将返回一个字符串。在 SQLAlchemy 中,“func.current_timestamp()” 被注册为返回 DATETIME 类型,因此此函数仍接收 SQLAlchemy 级别的结果处理。

线程/池行为

默认情况下,sqlite3 DBAPI 禁止在创建它的线程之外的线程中使用特定连接。随着 SQLite 的成熟,它在多线程下的行为已经改进,甚至包括选项,使得内存数据库可以在多个线程中使用。

线程禁止被称为“检查同一线程”,可以使用sqlite3参数check_same_thread进行控制,该参数将禁用或启用此检查。在使用基于文件的数据库时,SQLAlchemy 的默认行为是自动将check_same_thread设置为False,以确立与默认池类QueuePool的兼容性。

SQLAlchemy 的 pysqlite DBAPI 根据请求的 SQLite 数据库的类型以不同的方式建立连接池:

  • 当指定:memory: SQLite 数据库时,默认情况下方言将使用SingletonThreadPool。此池在每个线程中维护单个连接,因此当前线程内对引擎的所有访问都使用相同的:memory:数据库 - 其他线程将访问不同的:memory:数据库。check_same_thread参数默认为True

  • 当指定基于文件的数据库时,方言将使用QueuePool作为连接的源。同时,默认情况下将check_same_thread标志设置为False,除非被覆盖。

    自 2.0 版本更改:SQLite 文件数据库引擎现在默认使用QueuePool。以前使用的是NullPool。可以通过create_engine.poolclass参数指定使用NullPool类。

禁用文件数据库的连接池

可以通过为poolclass()参数指定NullPool实现来禁用基于文件的数据库的连接池:

from sqlalchemy import NullPool
engine = create_engine("sqlite:///myfile.db", poolclass=NullPool)

当使用NullPool实现时,由于QueuePool未实现连接重用,因此对于重复检出,NullPool实现会产生极小的性能开销。然而,如果应用程序遇到文件被锁定的问题,仍然可能有利于使用此类。

在多个线程中使用内存数据库

要在多线程场景中使用 :memory: 数据库,必须在线程之间共享同一个连接对象,因为数据库仅存在于该连接的范围内。 StaticPool 实现将全局维护单个连接,并且可以将 check_same_thread 标志传递给 Pysqlite 为 False

from sqlalchemy.pool import StaticPool
engine = create_engine('sqlite://',
                    connect_args={'check_same_thread':False},
                    poolclass=StaticPool)

请注意,要在多个线程中使用 :memory: 数据库,需要使用最近版本的 SQLite。

使用 SQLite 临时表

由于 SQLite 处理临时表的方式,如果希望在基于文件的 SQLite 数据库中跨多个连接池检出使用临时表(例如在使用 ORM Session 时,临时表应在 Session.commit()Session.rollback() 调用后继续存在),则必须使用维护单个连接的池。如果范围仅在当前线程内,则使用 SingletonThreadPool,如果此情况需要范围在多个线程内,则使用 StaticPool

# maintain the same connection per thread
from sqlalchemy.pool import SingletonThreadPool
engine = create_engine('sqlite:///mydb.db',
                    poolclass=SingletonThreadPool)

# maintain the same connection across all threads
from sqlalchemy.pool import StaticPool
engine = create_engine('sqlite:///mydb.db',
                    poolclass=StaticPool)

请注意,SingletonThreadPool 应配置为要使用的线程数;超出该数量的连接将以不确定的方式关闭。

处理混合字符串/二进制列

SQLite 数据库是弱类型的,因此当使用二进制值(在 Python 中表示为 b'some string')时,可能发生以下情况,即特定的 SQLite 数据库可以在不同行中返回数据值,其中某些值将由 Pysqlite 驱动程序返回为 b'' 值,而其他值将作为 Python 字符串返回,例如 '' 值。如果始终一致使用 SQLAlchemy 的 LargeBinary 数据类型,则不知道是否会发生此情况;但是如果特定的 SQLite 数据库具有使用 Pysqlite 驱动程序直接插入的数据,或者在使用后更改为 LargeBinary 的 SQLAlchemy String 类型时,该表将无法一致地读取,因为 SQLAlchemy 的 LargeBinary 数据类型不处理字符串,因此无法“编码”字符串格式的值。

要处理具有相同列中的混合字符串/二进制数据的 SQLite 表,请使用一个将逐个检查每行的自定义类型:

from sqlalchemy import String
from sqlalchemy import TypeDecorator

class MixedBinary(TypeDecorator):
    impl = String
    cache_ok = True

    def process_result_value(self, value, dialect):
        if isinstance(value, str):
            value = bytes(value, 'utf-8')
        elif value is not None:
            value = bytes(value)

        return value

然后在通常会使用LargeBinary的地方使用上述MixedBinary数据类型。

可序列化隔离/保存点/事务 DDL

在数据库锁定行为/并发性部分,我们提到 pysqlite 驱动程序的一系列问题,这些问题阻止 SQLite 的几个功能正常工作。 pysqlite DBAPI 驱动程序有几个长期存在的错误,影响其事务行为的正确性。在其默认操作模式下,SQLite 的功能,如可序列化隔离、事务 DDL 和 SAVEPOINT 支持是不起作用的,为了使用这些功能,必须采取解决方法。

问题实质上是驱动程序试图猜测用户意图,未能启动事务,有时会过早结束事务,以减少 SQLite 数据库的文件锁定行为,尽管 SQLite 本身对只读活动使用“共享”锁。

SQLAlchemy 选择默认情况下不更改此行为,因为这是 pysqlite 驱动程序的长期预期行为;如果 pysqlite 驱动程序尝试修复这些问��,那将更多地推动 SQLAlchemy 的默认值。

好消息是,通过几个事件,我们可以完全实现事务支持,通过完全禁用 pysqlite 的功能并自己发出 BEGIN。这是通过使用两个事件监听器实现的:

from sqlalchemy import create_engine, event

engine = create_engine("sqlite:///myfile.db")

@event.listens_for(engine, "connect")
def do_connect(dbapi_connection, connection_record):
    # disable pysqlite's emitting of the BEGIN statement entirely.
    # also stops it from emitting COMMIT before any DDL.
    dbapi_connection.isolation_level = None

@event.listens_for(engine, "begin")
def do_begin(conn):
    # emit our own BEGIN
    conn.exec_driver_sql("BEGIN")

警告

在使用上述配方时,建议不要在 SQLite 驱动程序上使用Connection.execution_options.isolation_level设置Connectioncreate_engine(),因为此函数必然也会改变“.isolation_level”设置。

在上面,我们拦截一个新的 pysqlite 连接并禁用任何事务集成。然后,在 SQLAlchemy 知道事务范围将开始的时候,我们自己发出"BEGIN"

当我们控制"BEGIN"时,我们还可以直接控制 SQLite 的锁定模式,通过将所需的锁定模式添加到我们的"BEGIN"中引入的开始事务

@event.listens_for(engine, "begin")
def do_begin(conn):
    conn.exec_driver_sql("BEGIN EXCLUSIVE")

另请参阅

开始事务 - 在 SQLite 网站上

sqlite3 SELECT 不会 BEGIN 事务 - 在 Python 错误跟踪器上

sqlite3 模块中断事务并可能损坏数据 - 在 Python 错误跟踪器上 ### 用户定义的函数

pysqlite 支持一个 create_function() 方法,允许我们在 Python 中创建自己的用户定义的函数 (UDFs),并直接在 SQLite 查询中使用它们。这些函数已与特定的 DBAPI 连接注册。

SQLAlchemy 使用基于文件的 SQLite 数据库的连接池,因此我们需要确保在创建连接时将 UDF 附加到连接。这通过事件监听器实现:

from sqlalchemy import create_engine
from sqlalchemy import event
from sqlalchemy import text

def udf():
    return "udf-ok"

engine = create_engine("sqlite:///./db_file")

@event.listens_for(engine, "connect")
def connect(conn, rec):
    conn.create_function("udf", 0, udf)

for i in range(5):
    with engine.connect() as conn:
        print(conn.scalar(text("SELECT UDF()")))
```  ## Aiosqlite

通过 aiosqlite 驱动程序支持 SQLite 数据库。

### DBAPI

aiosqlite 的文档和下载信息(如果适用)可在此处获得:[`pypi.org/project/aiosqlite/`](https://pypi.org/project/aiosqlite/)

### 连接

连接字符串:

```py
sqlite+aiosqlite:///file_path

aiosqlite 方言提供了对运行在 pysqlite 之上的 SQLAlchemy asyncio 接口的支持。

aiosqlite 是对 pysqlite 的封装,每个连接使用一个后台线程。它实际上不使用非阻塞 IO,因为 SQLite 数据库不是基于套接字的。但是它提供了一个可用于测试和原型设计的工作 asyncio 接口。

使用特殊的 asyncio 中介层,aiosqlite 方言可作为 SQLAlchemy asyncio 扩展包的后端使用。

通常应使用 create_async_engine() 引擎创建函数创建此方言:

from sqlalchemy.ext.asyncio import create_async_engine
engine = create_async_engine("sqlite+aiosqlite:///filename")

URL 通过所有参数传递给 pysqlite 驱动程序,因此所有连接参数与 Pysqlite 的相同。

用户定义的函数

aiosqlite 扩展了 pysqlite 以支持异步,因此我们可以在 Python 中创建自定义用户定义的函数 (UDFs),并直接在 SQLite 查询中使用它们,如此处所述:用户定义的函数。### Serializable isolation / Savepoints / Transactional DDL (asyncio 版本)

类似于 pysqlite,aiosqlite 不支持 SAVEPOINT 功能。

解决方案类似于 Serializable isolation / Savepoints / Transactional DDL。这是通过 async 中的事件监听器实现的:

from sqlalchemy import create_engine, event
from sqlalchemy.ext.asyncio import create_async_engine

engine = create_async_engine("sqlite+aiosqlite:///myfile.db")

@event.listens_for(engine.sync_engine, "connect")
def do_connect(dbapi_connection, connection_record):
    # disable aiosqlite's emitting of the BEGIN statement entirely.
    # also stops it from emitting COMMIT before any DDL.
    dbapi_connection.isolation_level = None

@event.listens_for(engine.sync_engine, "begin")
def do_begin(conn):
    # emit our own BEGIN
    conn.exec_driver_sql("BEGIN")

警告

在使用以上方案时,建议不要在 SQLite 驱动程序上使用 Connection.execution_options.isolation_level 设置 Connectioncreate_engine() ,因为该函数必然也会改变 “.isolation_level” 设置。## Pysqlcipher

通过 pysqlcipher 驱动程序支持 SQLite 数据库。

为支持利用 SQLCipher 后端的 DBAPI 提供方言。

连接中

连接字符串:

sqlite+pysqlcipher://:passphrase@/file_path[?kdf_iter=<iter>]

驱动程序

当前的方言选择逻辑是:

警告

截止到目前为止,pysqlcipher3pysqlcipher DBAPI 驱动程序不再维护;sqlcipher3 驱动程序似乎是当前的。为了未来的兼容性,可以使用任何兼容 pysqlcipher 的 DBAPI 如下所示:

import sqlcipher_compatible_driver

from sqlalchemy import create_engine

e = create_engine(
    "sqlite+pysqlcipher://:password@/dbname.db",
    module=sqlcipher_compatible_driver
)

这些驱动程序利用了 SQLCipher 引擎。该系统基本上引入了新的 PRAGMA 命令到 SQLite,这允许设置密码和其他加密参数,从而允许加密数据库文件。

连接字符串

连接字符串的格式在每个方面与 pysqlite 驱动程序的格式相同,除了现在接受“密码”字段,该字段应包含一个密码:

e = create_engine('sqlite+pysqlcipher://:testing@/foo.db')

对于绝对文件路径,应该使用两个前导斜杠作为数据库名:

e = create_engine('sqlite+pysqlcipher://:testing@//path/to/foo.db')

可以通过查询字符串传递一系列由 SQLCipher 支持的附加加密相关的 PRAGMA,如 www.zetetic.net/sqlcipher/sqlcipher-api/ 中所述,并且将导致每个新连接调用该 PRAGMA。目前,支持 cipherkdf_itercipher_page_sizecipher_use_hmac

e = create_engine('sqlite+pysqlcipher://:testing@/foo.db?cipher=aes-256-cfb&kdf_iter=64000')

警告

先前版本的 sqlalchemy 没有考虑到在 url 字符串中传递的与加密相关的 PRAGMA,这些 PRAGMA 被悄悄地忽略了。如果加密选项不匹配,这可能会导致打开由之前的 sqlalchemy 版本保存的文件时出错。

池行为

驱动程序对 pysqlite 的默认池行为进行了更改,如线程/池行为中所述。观察到 pysqlcipher 驱动程序连接速度比 pysqlite 驱动程序慢得多,很可能是由于加密开销,因此该方言在这里默认使用SingletonThreadPool实现,而不是 pysqlite 使用的NullPool池。与往常一样,可以使用create_engine.poolclass参数完全配置池实现;StaticPool可能更适合单线程使用,或者可以使用NullPool来防止未加密的连接被长时间保持打开,但新连接的启动时间较慢。

支持 SQLite 数据库。

以下表总结了当前数据库发布版本的支持水平。

支持的 SQLite 版本

支持类型 版本
在 CI 中完全测试 3.36.0
常规支持 3.12+
尽力而为 3.7.16+

DBAPI 支持

可用以下方言/DBAPI 选项。请参考各个 DBAPI 部分以获取连接信息。

  • pysqlite

  • aiosqlite

  • pysqlcipher

日期和时间类型

SQLite 没有内置的 DATE、TIME 或 DATETIME 类型,而 pysqlite 也没有提供将值在 Python datetime 对象和 SQLite 支持的格式之间转换的开箱即用功能。当使用 SQLite 时,SQLAlchemy 自己的DateTime和相关类型提供日期格式化和解析功能。实现类是DATETIMEDATETIME。这些类型将日期和时间表示为 ISO 格式的字符串,这也很好地支持排序。这些函数不依赖于典型的“libc”内部,因此完全支持历史日期。

确保文本亲和性

这些类型的 DDL 呈现是标准的 DATETIMEDATETIME 指示符。然而,这些类型也可以应用自定义的存储格式。当检测到存储格式不包含任何字母字符时,这些类型的 DDL 将呈现为 DATE_CHARTIME_CHARDATETIME_CHAR,以便列继续具有文本亲和性。

参见

类型亲和性 - SQLite 文档中的内容

确保文本亲和性

这些类型的 DDL 呈现是标准的 DATETIMEDATETIME 指示符。然而,这些类型也可以应用自定义的存储格式。当检测到存储格式不包含任何字母字符时,这些类型的 DDL 将呈现为 DATE_CHARTIME_CHARDATETIME_CHAR,以便列继续具有文本亲和性。

参见

类型亲和性 - SQLite 文档中的内容

SQLite 自动增量行为

关于 SQLite 的自动增量的背景信息请参阅:sqlite.org/autoinc.html

关键概念:

  • SQLite 具有隐式的“自动增量”功能,适用于任何使用“INTEGER PRIMARY KEY”来明确创建的非复合主键列。

  • SQLite 还具有显式的 “AUTOINCREMENT” 关键字,这与隐式自动增量功能 等同;不建议一般使用这个关键字。SQLAlchemy 不会呈现此关键字,除非使用特殊的特定于 SQLite 的指令(见下文)。但是,它仍然要求列的类型被命名为 “INTEGER”。

使用 AUTOINCREMENT 关键字

要在渲染 DDL 时在主键列上具体呈现 AUTOINCREMENT 关键字,请将 sqlite_autoincrement=True 标志添加到 Table 构造函数中:

Table('sometable', metadata,
        Column('id', Integer, primary_key=True),
        sqlite_autoincrement=True)

允许除 Integer/INTEGER 之外的 SQLAlchemy 类型具有自动增量行为

SQLite 的类型模型基于命名约定。这意味着包含子字符串 "INT" 的任何类型名称都将被确定为“整数亲和性”。一个名为 "BIGINT""SPECIAL_INT" 或甚至 "XYZINTQPR" 的类型都将被 SQLite 视为“整数”亲和性。然而,无论隐式还是显式启用了 SQLite 的自动增量功能,列类型的名称都必须正好是字符串 "INTEGER"。因此,如果应用程序使用 BigInteger 作为主键的类型,在 SQLite 上,当在发出初始的 CREATE TABLE 语句时,这个类型将需要被渲染为名称 "INTEGER",以便自动增量行为可用。

实现此目标的一种方法是仅在 SQLite 上使用 TypeEngine.with_variant() 使用 Integer:

table = Table(
    "my_table", metadata,
    Column("id", BigInteger().with_variant(Integer, "sqlite"), primary_key=True)
)

另一种方法是使用 BigInteger 的子类,当编译针对 SQLite 时,重写其 DDL 名称为 INTEGER

from sqlalchemy import BigInteger
from sqlalchemy.ext.compiler import compiles

class SLBigInteger(BigInteger):
    pass

@compiles(SLBigInteger, 'sqlite')
def bi_c(element, compiler, **kw):
    return "INTEGER"

@compiles(SLBigInteger)
def bi_c(element, compiler, **kw):
    return compiler.visit_BIGINT(element, **kw)

table = Table(
    "my_table", metadata,
    Column("id", SLBigInteger(), primary_key=True)
)

另请参阅

TypeEngine.with_variant()

自定义 SQL 构造和编译扩展

SQLite 版本 3 中的数据类型

使用 AUTOINCREMENT 关键字

要在渲染 DDL 时特别呈现主键列上的 AUTOINCREMENT 关键字,请向 Table 构造添加标志 sqlite_autoincrement=True

Table('sometable', metadata,
        Column('id', Integer, primary_key=True),
        sqlite_autoincrement=True)

允许除 Integer/INTEGER 外的 SQLAlchemy 类型具有自增行为

SQLite 的类型模型基于命名约定。除其他外,这意味着包含子字符串 "INT" 的任何类型名称都将被确定为“整数亲和性”。类型名称为 "BIGINT""SPECIAL_INT" 甚至 "XYZINTQPR" 的类型,SQLite 都会将其视为“整数”亲和性。然而,无论是隐式还是显式启用的 SQLite 自增特性,都要求列的类型名称正好是字符串"INTEGER"。因此,如果应用程序使用类似 BigInteger 的类型作为主键,在 SQLite 上,此类型在发出初始的 CREATE TABLE 语句时需要呈现为名称 "INTEGER",以便自增行为可用。

实现此目标的一种方法是仅在 SQLite 上使用 TypeEngine.with_variant() 使用 Integer:

table = Table(
    "my_table", metadata,
    Column("id", BigInteger().with_variant(Integer, "sqlite"), primary_key=True)
)

另一种方法是使用 BigInteger 的子类,当编译针对 SQLite 时,重写其 DDL 名称为 INTEGER

from sqlalchemy import BigInteger
from sqlalchemy.ext.compiler import compiles

class SLBigInteger(BigInteger):
    pass

@compiles(SLBigInteger, 'sqlite')
def bi_c(element, compiler, **kw):
    return "INTEGER"

@compiles(SLBigInteger)
def bi_c(element, compiler, **kw):
    return compiler.visit_BIGINT(element, **kw)

table = Table(
    "my_table", metadata,
    Column("id", SLBigInteger(), primary_key=True)
)

另请参阅

TypeEngine.with_variant()

自定义 SQL 构造和编译扩展

SQLite 版本 3 中的数据类型

数据库锁定行为 / 并发

SQLite 并不适用于高度写并发性。数据库本身,作为一个文件,在事务内的写操作期间完全被锁定,这意味着在此期间仅有一个“连接”(实际上是一个文件句柄)对数据库具有独占访问权限 - 在此期间所有其他“连接”都将被阻塞。

Python DBAPI 规范还要求一个始终处于事务中的连接模型;没有 connection.begin() 方法,只有 connection.commit()connection.rollback(),在这之后立即开始一个新事务。这似乎暗示着 SQLite 驱动理论上只允许在任何时候对特定数据库文件进行单个文件句柄的访问;然而,SQLite 本身以及 pysqlite 驱动程序中有几个因素大大放宽了这个限制。

然而,无论使用什么锁定模式,一旦启动事务并且已经发出了 DML(例如 INSERT、UPDATE、DELETE),SQLite 都会锁定数据库文件,这将至少在其他事务也试图发出 DML 的时候阻塞其他事务。默认情况下,在此阻塞的时间长度非常短,超时后会出现错误。

当与 SQLAlchemy ORM 结合使用时,这种行为变得更加关键。SQLAlchemy 的 Session 对象默认在事务内运行,并且使用其自动刷新模型,可能会在任何 SELECT 语句之前发出 DML。这可能导致 SQLite 数据库比预期更快地锁定。SQLite 和 pysqlite 驱动程序的锁定模式可以在一定程度上被操纵,但应注意,要想在 SQLite 中实现高度的写并发性是一场失败的战斗。

欲了解 SQLite 的设计缺乏写并发性的更多信息,请参阅在哪些情况下另一个 RDBMS 可能更适合使用 - 高并发性,页面底部。

以下子节介绍了受 SQLite 的基于文件的架构影响的领域,此外,通常在使用 pysqlite 驱动程序时需要一些解决方法。

事务隔离级别 / 自动提交

SQLite 以一种非标准的方式支持“事务隔离”,沿着两个轴线。一个是PRAGMA read_uncommitted 指令。这个设置可以基本上在 SQLite 的默认模式 SERIALIZABLE 隔离和一个通常称为 READ UNCOMMITTED 的“脏读”隔离模式之间切换。

SQLAlchemy 使用create_engine.isolation_level参数来连接到此 PRAGMA 语句create_engine()。在与 SQLite 一起使用此参数的有效值是"SERIALIZABLE""READ UNCOMMITTED",分别对应于 0 和 1 的值。SQLite 默认为SERIALIZABLE,但其行为受到 pysqlite 驱动程序的默认行为的影响。

使用 pysqlite 驱动程序时,还可以使用"AUTOCOMMIT"隔离级别,该级别将通过 DBAPI 连接的.isolation_level属性更改 pysqlite 连接,并将其设置为 None 以进行设置的持续时间。

版本 1.3.16 中的新功能:在使用 pysqlite/sqlite3 SQLite 驱动程序时增加了对 SQLite AUTOCOMMIT 隔离级别的支持。

SQLite 的事务锁定受影响的另一个轴是通过使用的BEGIN语句的性质。这三种类型是“延迟”、“立即”和“独占”,如开始事务所述。直接的BEGIN语句使用“延迟”模式,在第一次读取或写入操作之前不锁定数据库文件,并且读取访问在第一次写入操作之前仍然对其他事务开放。但需要再次强调的是,pysqlite 驱动器通过甚至不发出 BEGIN直到第一次写入操作来干扰此行为。

警告

SQLite 的事务范围受到 pysqlite 驱动程序中未解决的问题的影响,该问题将 BEGIN 语句推迟到比通常可行的更大程度。有关解决此行为的技术,请参阅部分可序列化隔离/保存点/事务 DDL 或可序列化隔离/保存点/事务 DDL(asyncio 版本)。

请参阅

设置事务隔离级别,包括 DBAPI 自动提交

INSERT/UPDATE/DELETE…RETURNING

SQLite 方言支持 SQLite 3.35 的INSERT|UPDATE|DELETE..RETURNING语法。在某些情况下,INSERT..RETURNING可能会自动使用,以获取新生成的标识符,而不是传统方法中使用cursor.lastrowid,但目前仍然推荐对于简单的单语句情况使用cursor.lastrowid,因为其性能更好。

要指定显式的RETURNING子句,请在每个语句上使用_UpdateBase.returning()方法:

# INSERT..RETURNING
result = connection.execute(
    table.insert().
    values(name='foo').
    returning(table.c.col1, table.c.col2)
)
print(result.all())

# UPDATE..RETURNING
result = connection.execute(
    table.update().
    where(table.c.name=='foo').
    values(name='bar').
    returning(table.c.col1, table.c.col2)
)
print(result.all())

# DELETE..RETURNING
result = connection.execute(
    table.delete().
    where(table.c.name=='foo').
    returning(table.c.col1, table.c.col2)
)
print(result.all())

版本 2.0 中的新功能:增加了对 SQLite RETURNING 的支持

保存点支持

SQLite 支持 SAVEPOINT,仅在启动事务后才能运行。SQLAlchemy 的 SAVEPOINT 支持可在 Core 级别使用 Connection.begin_nested() 方法,在 ORM 级别使用 Session.begin_nested()。但是,除非采取解决方法,否则 SAVEPOINT 在 pysqlite 中将无法工作。

警告

pysqlite 和 aiosqlite 驱动存在未解决的问题,这些问题将 BEGIN 语句推迟到一个更大程度上比通常可行的程度。有关绕过此行为的技术,请参见 Serializable isolation / Savepoints / Transactional DDL 和 Serializable isolation / Savepoints / Transactional DDL (asyncio version) 部分。

事务性 DDL

SQLite 数据库还支持事务性 DDL。在这种情况下,pysqlite 驱动不仅在检测到 DDL 时无法启动事务,还会结束任何现有事务,因此需要采取解决方法。

警告

pysqlite 驱动中存在未解决的问题影响了 SQLite 的事务性 DDL,当遇到 DDL 时,该驱动器未发出 BEGIN 并且还强制执行 COMMIT 来取消任何事务。有关绕过此行为的技术,请参见 Serializable isolation / Savepoints / Transactional DDL 部分。

外键支持

当发出用于表的 CREATE 语句时,SQLite 支持 FOREIGN KEY 语法,但是默认情况下,这些约束对表的操作没有任何影响。

在 SQLite 上进行约束检查有三个先决条件:

  • 必须使用至少版本 3.6.19 的 SQLite

  • 必须在编译 SQLite 库时 没有 启用 SQLITE_OMIT_FOREIGN_KEY 或 SQLITE_OMIT_TRIGGER 符号。

  • 必须在所有连接上发出 PRAGMA foreign_keys = ON 语句,包括对 MetaData.create_all() 的初始调用。

SQLAlchemy 允许通过事件的使用自动发出 PRAGMA 语句以进行新连接:

from sqlalchemy.engine import Engine
from sqlalchemy import event

@event.listens_for(Engine, "connect")
def set_sqlite_pragma(dbapi_connection, connection_record):
    cursor = dbapi_connection.cursor()
    cursor.execute("PRAGMA foreign_keys=ON")
    cursor.close()

警告

当启用 SQLite 外键时,不可能对包含相互依赖的外键约束的表发出 CREATE 或 DROP 语句;要发出这些表的 DDL,需要单独使用 ALTER TABLE 创建或删除这些约束,而 SQLite 不支持这一点。

另请参阅

SQLite 外键支持 - 在 SQLite 网站上。

事件 - SQLAlchemy 事件 API。

通过 ALTER 创建/删除外键约束 - 有关 SQLAlchemy 处理的更多信息

互相依赖的外键约束。

对约束的 ON CONFLICT 支持

另请参见

本节描述了 SQLite 中“ON CONFLICT”的 DDL 版本,该版本出现在 CREATE TABLE 语句中。有关应用于 INSERT 语句的“ON CONFLICT”,请参见 INSERT…ON CONFLICT (Upsert)。

SQLite 支持一个名为 ON CONFLICT 的非标准 DDL 子句,可应用于主键、唯一、检查和非空约束。在 DDL 中,它要么在“CONSTRAINT”子句中呈现,要么在目标约束的位置取决于列定义本身。要在 DDL 中呈现此子句,可以在PrimaryKeyConstraintUniqueConstraintCheckConstraint对象中指定扩展参数sqlite_on_conflict,并在Column对象中,有单独的参数sqlite_on_conflict_not_nullsqlite_on_conflict_primary_keysqlite_on_conflict_unique,分别对应于可以从Column对象指示的三种相关约束类型。

另请参见

冲突时执行 - 在 SQLite 文档中

版本 1.3 中的新功能。

sqlite_on_conflict参数接受一个字符串参数,该参数只是要选择的解决方案名称,在 SQLite 上可以是 ROLLBACK、ABORT、FAIL、IGNORE 和 REPLACE 中的一个。例如,要添加一个指定 IGNORE 算法的唯一约束:

some_table = Table(
    'some_table', metadata,
    Column('id', Integer, primary_key=True),
    Column('data', Integer),
    UniqueConstraint('id', 'data', sqlite_on_conflict='IGNORE')
)

以上将 CREATE TABLE DDL 呈现为:

CREATE TABLE some_table (
    id INTEGER NOT NULL,
    data INTEGER,
    PRIMARY KEY (id),
    UNIQUE (id, data) ON CONFLICT IGNORE
)

当使用Column.unique标志向单个列添加唯一约束时,也可以向Column添加sqlite_on_conflict_unique参数,该参数将添加到 DDL 中的唯一约束中:

some_table = Table(
    'some_table', metadata,
    Column('id', Integer, primary_key=True),
    Column('data', Integer, unique=True,
           sqlite_on_conflict_unique='IGNORE')
)

渲染:

CREATE TABLE some_table (
    id INTEGER NOT NULL,
    data INTEGER,
    PRIMARY KEY (id),
    UNIQUE (data) ON CONFLICT IGNORE
)

要应用 FAIL 算法以满足 NOT NULL 约束,使用sqlite_on_conflict_not_null

some_table = Table(
    'some_table', metadata,
    Column('id', Integer, primary_key=True),
    Column('data', Integer, nullable=False,
           sqlite_on_conflict_not_null='FAIL')
)

这将使列内联 ON CONFLICT 短语:

CREATE TABLE some_table (
    id INTEGER NOT NULL,
    data INTEGER NOT NULL ON CONFLICT FAIL,
    PRIMARY KEY (id)
)

同样,对于内联主键,使用sqlite_on_conflict_primary_key

some_table = Table(
    'some_table', metadata,
    Column('id', Integer, primary_key=True,
           sqlite_on_conflict_primary_key='FAIL')
)

SQLAlchemy 将主键约束单独呈现,因此冲突解决算法应用于约束本身:

CREATE TABLE some_table (
    id INTEGER NOT NULL,
    PRIMARY KEY (id) ON CONFLICT FAIL
)

插入…冲突时执行(Upsert)

另请参见

本节描述了 SQLite 的“ON CONFLICT”的 DML 版本,它发生在 INSERT 语句中。有关应用于 CREATE TABLE 语句的“ON CONFLICT”,请参见约束的 ON CONFLICT 支持。

从版本 3.24.0 开始,SQLite 支持通过 INSERT 语句的 ON CONFLICT 子句进行行的“upserts”(更新或插入)到表中。仅当候选行不违反任何唯一或主键约束时才会插入该行。在唯一约束违反的情况下,可以发生次要操作,可以是“DO UPDATE”,表示应更新目标行中的数据,或者是“DO NOTHING”,表示默默地跳过此行。

冲突是使用现有唯一约束和索引的列确定的。这些约束通过说明组成索引的列和条件来确定。

SQLAlchemy 通过 SQLite 特定的 insert() 函数提供 ON CONFLICT 支持,该函数提供生成方法 Insert.on_conflict_do_update()Insert.on_conflict_do_nothing()

>>> from sqlalchemy.dialects.sqlite import insert

>>> insert_stmt = insert(my_table).values(
...     id='some_existing_id',
...     data='inserted value')

>>> do_update_stmt = insert_stmt.on_conflict_do_update(
...     index_elements=['id'],
...     set_=dict(data='updated value')
... )

>>> print(do_update_stmt)
INSERT  INTO  my_table  (id,  data)  VALUES  (?,  ?)
ON  CONFLICT  (id)  DO  UPDATE  SET  data  =  ?
>>> do_nothing_stmt = insert_stmt.on_conflict_do_nothing(
...     index_elements=['id']
... )

>>> print(do_nothing_stmt)
INSERT  INTO  my_table  (id,  data)  VALUES  (?,  ?)
ON  CONFLICT  (id)  DO  NOTHING 

新版本 1.4 中新增。

另请参阅

Upsert - SQLite 文档中的内容。

指定目标

两种方法都使用列推断冲突的“目标”:

  • Insert.on_conflict_do_update.index_elements 参数指定包含字符串列名称、Column 对象和/或 SQL 表达式元素的序列,这些元素将标识唯一索引或唯一约束。

  • 当使用 Insert.on_conflict_do_update.index_elements 来推断索引时,还可以通过指定 Insert.on_conflict_do_update.index_where 参数推断出部分索引:

    >>> stmt = insert(my_table).values(user_email='a@b.com', data='inserted data')
    
    >>> do_update_stmt = stmt.on_conflict_do_update(
    ...     index_elements=[my_table.c.user_email],
    ...     index_where=my_table.c.user_email.like('%@gmail.com'),
    ...     set_=dict(data=stmt.excluded.data)
    ...     )
    
    >>> print(do_update_stmt)
    INSERT  INTO  my_table  (data,  user_email)  VALUES  (?,  ?)
    ON  CONFLICT  (user_email)
    WHERE  user_email  LIKE  '%@gmail.com'
    DO  UPDATE  SET  data  =  excluded.data 
    

SET 子句

使用 ON CONFLICT...DO UPDATE 来执行已经存在行的更新,使用任何组合的新值以及来自所提议插入的值。这些值使用 Insert.on_conflict_do_update.set_ 参数指定。该参数接受一个包含直接 UPDATE 值的字典:

>>> stmt = insert(my_table).values(id='some_id', data='inserted value')

>>> do_update_stmt = stmt.on_conflict_do_update(
...     index_elements=['id'],
...     set_=dict(data='updated value')
... )

>>> print(do_update_stmt)
INSERT  INTO  my_table  (id,  data)  VALUES  (?,  ?)
ON  CONFLICT  (id)  DO  UPDATE  SET  data  =  ? 

警告

Insert.on_conflict_do_update() 方法 考虑 Python 端的默认 UPDATE 值或生成函数,例如,使用 Column.onupdate 指定的那些。除非在 Insert.on_conflict_do_update.set_ 字典中手动指定,否则这些值不会在 ON CONFLICT 类型的 UPDATE 中使用。

使用被排除的 INSERT 值进行更新

为了引用所提议的插入行,特殊别名 Insert.excluded 可以作为 Insert 对象的属性使用;该对象在列上创建了一个 “excluded.” 前缀,它通知 DO UPDATE 使用将插入的值更新行,如果约束没有失败的话将会插入的值:

>>> stmt = insert(my_table).values(
...     id='some_id',
...     data='inserted value',
...     author='jlh'
... )

>>> do_update_stmt = stmt.on_conflict_do_update(
...     index_elements=['id'],
...     set_=dict(data='updated value', author=stmt.excluded.author)
... )

>>> print(do_update_stmt)
INSERT  INTO  my_table  (id,  data,  author)  VALUES  (?,  ?,  ?)
ON  CONFLICT  (id)  DO  UPDATE  SET  data  =  ?,  author  =  excluded.author 

附加的 WHERE 条件

Insert.on_conflict_do_update() 方法还接受使用 Insert.on_conflict_do_update.where 参数的 WHERE 子句,这将限制接收 UPDATE 的行:

>>> stmt = insert(my_table).values(
...     id='some_id',
...     data='inserted value',
...     author='jlh'
... )

>>> on_update_stmt = stmt.on_conflict_do_update(
...     index_elements=['id'],
...     set_=dict(data='updated value', author=stmt.excluded.author),
...     where=(my_table.c.status == 2)
... )
>>> print(on_update_stmt)
INSERT  INTO  my_table  (id,  data,  author)  VALUES  (?,  ?,  ?)
ON  CONFLICT  (id)  DO  UPDATE  SET  data  =  ?,  author  =  excluded.author
WHERE  my_table.status  =  ? 

使用 DO NOTHING 跳过行

ON CONFLICT 可以用来完全跳过插入行,如果任何与唯一约束发生冲突的话;下面通过使用 Insert.on_conflict_do_nothing() 方法进行了说明:

>>> stmt = insert(my_table).values(id='some_id', data='inserted value')
>>> stmt = stmt.on_conflict_do_nothing(index_elements=['id'])
>>> print(stmt)
INSERT  INTO  my_table  (id,  data)  VALUES  (?,  ?)  ON  CONFLICT  (id)  DO  NOTHING 

如果使用 DO NOTHING 而没有指定任何列或约束,则会跳过发生的任何唯一性冲突的 INSERT:

>>> stmt = insert(my_table).values(id='some_id', data='inserted value')
>>> stmt = stmt.on_conflict_do_nothing()
>>> print(stmt)
INSERT  INTO  my_table  (id,  data)  VALUES  (?,  ?)  ON  CONFLICT  DO  NOTHING 

指定目标

两种方法都使用列推断提供冲突的 “目标”:

  • Insert.on_conflict_do_update.index_elements 参数指定一个序列,包含字符串列名、Column 对象和/或 SQL 表达式元素,用于标识唯一索引或唯一约束。

  • 当使用 Insert.on_conflict_do_update.index_elements 推断索引时,还可以通过指定 Insert.on_conflict_do_update.index_where 参数来推断部分索引。

    >>> stmt = insert(my_table).values(user_email='a@b.com', data='inserted data')
    
    >>> do_update_stmt = stmt.on_conflict_do_update(
    ...     index_elements=[my_table.c.user_email],
    ...     index_where=my_table.c.user_email.like('%@gmail.com'),
    ...     set_=dict(data=stmt.excluded.data)
    ...     )
    
    >>> print(do_update_stmt)
    INSERT  INTO  my_table  (data,  user_email)  VALUES  (?,  ?)
    ON  CONFLICT  (user_email)
    WHERE  user_email  LIKE  '%@gmail.com'
    DO  UPDATE  SET  data  =  excluded.data 
    

SET 子句

ON CONFLICT...DO UPDATE 用于对已存在的行进行更新,可以使用新值与插入提议中的任意组合值。这些值使用 Insert.on_conflict_do_update.set_ 参数指定。此参数接受一个字典,其中包含 UPDATE 的直接值:

>>> stmt = insert(my_table).values(id='some_id', data='inserted value')

>>> do_update_stmt = stmt.on_conflict_do_update(
...     index_elements=['id'],
...     set_=dict(data='updated value')
... )

>>> print(do_update_stmt)
INSERT  INTO  my_table  (id,  data)  VALUES  (?,  ?)
ON  CONFLICT  (id)  DO  UPDATE  SET  data  =  ? 

警告

Insert.on_conflict_do_update() 方法不会考虑 Python 端默认的 UPDATE 值或生成函数,例如使用 Column.onupdate 指定的值。除非这些值在 Insert.on_conflict_do_update.set_ 字典中手动指定,否则这些值不会用于 ON CONFLICT 类型的 UPDATE。

使用插入的排除值进行更新

为了引用插入提议的行,特殊别名 Insert.excluded 可作为 Insert 对象的属性使用;此对象在列上创建一个“excluded.”前缀,该前缀告知 DO UPDATE 使用将在约束失败时插入的值更新行:

>>> stmt = insert(my_table).values(
...     id='some_id',
...     data='inserted value',
...     author='jlh'
... )

>>> do_update_stmt = stmt.on_conflict_do_update(
...     index_elements=['id'],
...     set_=dict(data='updated value', author=stmt.excluded.author)
... )

>>> print(do_update_stmt)
INSERT  INTO  my_table  (id,  data,  author)  VALUES  (?,  ?,  ?)
ON  CONFLICT  (id)  DO  UPDATE  SET  data  =  ?,  author  =  excluded.author 

附加的 WHERE 条件

Insert.on_conflict_do_update() 方法还接受使用 Insert.on_conflict_do_update.where 参数的 WHERE 子句,这将限制那些接收 UPDATE 的行:

>>> stmt = insert(my_table).values(
...     id='some_id',
...     data='inserted value',
...     author='jlh'
... )

>>> on_update_stmt = stmt.on_conflict_do_update(
...     index_elements=['id'],
...     set_=dict(data='updated value', author=stmt.excluded.author),
...     where=(my_table.c.status == 2)
... )
>>> print(on_update_stmt)
INSERT  INTO  my_table  (id,  data,  author)  VALUES  (?,  ?,  ?)
ON  CONFLICT  (id)  DO  UPDATE  SET  data  =  ?,  author  =  excluded.author
WHERE  my_table.status  =  ? 

使用 DO NOTHING 跳过行

ON CONFLICT 可以用于完全跳过插入行,如果发生与唯一约束的冲突;以下是使用 Insert.on_conflict_do_nothing() 方法进行说明:

>>> stmt = insert(my_table).values(id='some_id', data='inserted value')
>>> stmt = stmt.on_conflict_do_nothing(index_elements=['id'])
>>> print(stmt)
INSERT  INTO  my_table  (id,  data)  VALUES  (?,  ?)  ON  CONFLICT  (id)  DO  NOTHING 

如果 DO NOTHING 在不指定任何列或约束的情况下使用,则会跳过发生的任何唯一违规的 INSERT:

>>> stmt = insert(my_table).values(id='some_id', data='inserted value')
>>> stmt = stmt.on_conflict_do_nothing()
>>> print(stmt)
INSERT  INTO  my_table  (id,  data)  VALUES  (?,  ?)  ON  CONFLICT  DO  NOTHING 

类型反射

SQLite 类型与大多数其他数据库后端不同,在于类型的字符串名称通常不是一对一地对应于一个“类型”。相反,SQLite 将每列的类型行为链接到五种所谓的“类型亲和性”之一,基于类型的字符串匹配模式。

当 SQLAlchemy 的反射过程检查类型时,它使用一个简单的查找表将返回的关键字链接到提供的 SQLAlchemy 类型。这个查找表存在于 SQLite 方言中,就像存在于所有其他方言中一样。然而,当某个特定类型名称未在查找映射中找到时,SQLite 方言有一个不同的“回退”例程;它实现了位于 www.sqlite.org/datatype3.html 第 2.1 节的 SQLite “类型亲和性”方案。

提供的类型映射将直接从以下类型的精确字符串名称匹配进行关联:

BIGINTBLOBBOOLEANBOOLEANCHARDATEDATETIMEFLOATDECIMALFLOATINTEGERINTEGERNUMERICREALSMALLINTTEXTTIMETIMESTAMPVARCHARNVARCHARNCHAR

当类型名称与上述类型之一不匹配时,将使用“类型亲和性”查找:

  • 如果类型名称包含字符串INT,则返回INTEGER类型。

  • 如果类型名称包含字符串CHARCLOBTEXT,则返回TEXT类型。

  • 如果类型名称包含字符串BLOB,则返回NullType类型。

  • 如果类型名称包含字符串REALFLOADOUB,则返回REAL类型。

  • 否则,使用NUMERIC类型。

部分索引

可以使用 DDL 系统指定带有 WHERE 子句的部分索引,例如使用参数sqlite_where

tbl = Table('testtbl', m, Column('data', Integer))
idx = Index('test_idx1', tbl.c.data,
            sqlite_where=and_(tbl.c.data > 5, tbl.c.data < 10))

在创建时,索引将被渲染为:

CREATE INDEX test_idx1 ON testtbl (data)
WHERE data > 5 AND data < 10

点分列名

不推荐使用显式带有句点的表名或列名。虽然这对于关系数据库来说通常是个坏主意,因为句点是一个语法上重要的字符,但 SQLite 驱动在 SQLite 版本 3.10.0 之前存在一个 bug,要求 SQLAlchemy 在结果集中滤掉这些句点。

这个 bug 完全不在 SQLAlchemy 的范围之内,可以用以下方式加以说明:

import sqlite3

assert sqlite3.sqlite_version_info < (3, 10, 0), "bug is fixed in this version"

conn = sqlite3.connect(":memory:")
cursor = conn.cursor()

cursor.execute("create table x (a integer, b integer)")
cursor.execute("insert into x (a, b) values (1, 1)")
cursor.execute("insert into x (a, b) values (2, 2)")

cursor.execute("select x.a, x.b from x")
assert [c[0] for c in cursor.description] == ['a', 'b']

cursor.execute('''
 select x.a, x.b from x where a=1
 union
 select x.a, x.b from x where a=2
''')
assert [c[0] for c in cursor.description] == ['a', 'b'], \
    [c[0] for c in cursor.description]

第二个断言失败:

Traceback (most recent call last):
  File "test.py", line 19, in <module>
    [c[0] for c in cursor.description]
AssertionError: ['x.a', 'x.b']

在上述情况下,驱动程序错误地报告包括表名在内的列名,这与 UNION 不在时完全不一致。

SQLAlchemy 依赖于列名在匹配原始语句时的可预测性,因此 SQLAlchemy 方言别无选择,只能将这些列名滤除:

from sqlalchemy import create_engine

eng = create_engine("sqlite://")
conn = eng.connect()

conn.exec_driver_sql("create table x (a integer, b integer)")
conn.exec_driver_sql("insert into x (a, b) values (1, 1)")
conn.exec_driver_sql("insert into x (a, b) values (2, 2)")

result = conn.exec_driver_sql("select x.a, x.b from x")
assert result.keys() == ["a", "b"]

result = conn.exec_driver_sql('''
 select x.a, x.b from x where a=1
 union
 select x.a, x.b from x where a=2
''')
assert result.keys() == ["a", "b"]

请注意,尽管 SQLAlchemy 过滤掉了句点,这两个名称仍然是可寻址的

>>> row = result.first()
>>> row["a"]
1
>>> row["x.a"]
1
>>> row["b"]
1
>>> row["x.b"]
1

因此,SQLAlchemy 应用的解决方法只会影响公共 API 中的 CursorResult.keys()Row.keys()。在强制使用包含句点的列名,并且需要 CursorResult.keys()Row.keys() 返回这些带点的名称不经修改的非常特殊情况下,可以在每个 Connection 上提供 sqlite_raw_colnames 执行选项:

result = conn.execution_options(sqlite_raw_colnames=True).exec_driver_sql('''
 select x.a, x.b from x where a=1
 union
 select x.a, x.b from x where a=2
''')
assert result.keys() == ["x.a", "x.b"]

或者在每个 Engine 上:

engine = create_engine("sqlite://", execution_options={"sqlite_raw_colnames": True})

使用每个 Engine 的执行选项时,请注意使用 UNION 的 Core 和 ORM 查询可能无法正常工作

SQLite 特定的表选项

CREATE TABLE 的一种选项直接由 SQLite 方言支持,与 Table 结构配合使用:

  • WITHOUT ROWID

    Table("some_table", metadata, ..., sqlite_with_rowid=False)
    

另请参阅

SQLite CREATE TABLE 选项

反映内部模式表

返回表列表的反射方法将省略所谓的“SQLite 内部模式对象”名称,这些对象被 SQLite 视为任何以 sqlite_ 为前缀的对象名称。这种对象的示例是在使用 AUTOINCREMENT 列参数时生成的 sqlite_sequence 表。为了返回这些对象,可以向诸如 MetaData.reflect()Inspector.get_table_names() 这样的方法传递参数 sqlite_include_internal=True

新版本 2.0 中新增了 sqlite_include_internal=True 参数。以前,这些表不被 SQLAlchemy 反射方法忽略。

注意

sqlite_include_internal 参数不引用存在于 sqlite_master 等模式中的 “系统” 表。

另请参阅

SQLite 内部模式对象 - SQLite 文档中。

SQLite 数据类型

与所有 SQLAlchemy 方言一样,已知与 SQLite 兼容的所有大写类型都可以从顶级方言导入,无论它们来自 sqlalchemy.types 还是来自本地方言:

from sqlalchemy.dialects.sqlite import (
    BLOB,
    BOOLEAN,
    CHAR,
    DATE,
    DATETIME,
    DECIMAL,
    FLOAT,
    INTEGER,
    NUMERIC,
    JSON,
    SMALLINT,
    TEXT,
    TIME,
    TIMESTAMP,
    VARCHAR,
)
对象名称 描述
DATE 使用字符串在 SQLite 中表示 Python date 对象。
DATETIME 使用字符串在 SQLite 中表示 Python datetime 对象。
JSON SQLite JSON 类型。
TIME 使用字符串在 SQLite 中表示 Python time 对象。
class sqlalchemy.dialects.sqlite.DATETIME

使用字符串在 SQLite 中表示 Python datetime 对象。

默认字符串存储格式为:

"%(year)04d-%(month)02d-%(day)02d  %(hour)02d:%(minute)02d:%(second)02d.%(microsecond)06d"

例如:

2021-03-15 12:05:57.105542

默认情况下,输入的存储格式使用 Python datetime.fromisoformat() 函数进行解析。

新版本 2.0 中已更改:默认日期时间字符串解析使用 datetime.fromisoformat()

可以使用 storage_formatregexp 参数在一定程度上自定义存储格式,例如:

import re
from sqlalchemy.dialects.sqlite import DATETIME

dt = DATETIME(storage_format="%(year)04d/%(month)02d/%(day)02d "
                             "%(hour)02d:%(minute)02d:%(second)02d",
              regexp=r"(\d+)/(\d+)/(\d+) (\d+)-(\d+)-(\d+)"
)

参数:

  • storage_format - 应用于带有年、月、日、小时、分钟、秒和微秒键的字典的格式字符串。

  • regexp - 应用于输入结果行的正则表达式,用于替换使用 datetime.fromisoformat() 解析输入字符串。如果正则表达式包含命名组,则结果匹配字典将作为关键字参数应用于 Python datetime() 构造函数。否则,如果使用位置组,则通过 *map(int, match_obj.groups(0)) 调用 datetime() 构造函数进行位置参数传递。

类签名

sqlalchemy.dialects.sqlite.DATETIME (sqlalchemy.dialects.sqlite.base._DateTimeMixin, sqlalchemy.types.DateTime)

class sqlalchemy.dialects.sqlite.DATE

使用字符串在 SQLite 中表示 Python 日期对象。

默认字符串存储格式为:

"%(year)04d-%(month)02d-%(day)02d"

例如:

2011-03-15

默认情况下,输入的存储格式使用 Python date.fromisoformat() 函数进行解析。

新版本 2.0 中已更改:默认日期字符串解析使用 date.fromisoformat()

可以使用 storage_formatregexp 参数在一定程度上自定义存储格式,例如:

import re
from sqlalchemy.dialects.sqlite import DATE

d = DATE(
        storage_format="%(month)02d/%(day)02d/%(year)04d",
        regexp=re.compile("(?P<month>\d+)/(?P<day>\d+)/(?P<year>\d+)")
    )

参数:

  • storage_format - 应用于带有年、月和日键的字典的格式字符串。

  • regexp – 将应用于传入结果行的正则表达式,替换使用 date.fromisoformat() 来解析传入字符串。如果 regexp 包含命名组,则生成的匹配字典将作为关键字参数应用于 Python date() 构造函数。否则,如果使用位置组,则通过 *map(int, match_obj.groups(0)) 将调用 date() 构造函数以位置参数形式。

类签名

sqlalchemy.dialects.sqlite.DATE (sqlalchemy.dialects.sqlite.base._DateTimeMixin, sqlalchemy.types.Date)

class sqlalchemy.dialects.sqlite.JSON

SQLite JSON 类型。

SQLite 从版本 3.9 开始支持 JSON,通过其 JSON1 扩展。请注意,JSON1 是一个可加载扩展,因此可能不可用,或者可能需要运行时加载。

当基本 JSON 数据类型用于 SQLite 后端时,JSON 会自动使用。

另请参阅

JSON - 通用跨平台 JSON 数据类型的主要文档。

JSON 类型支持将 JSON 值持久化,同时通过在数据库级别将 JSON_EXTRACT 函数包装在 JSON_QUOTE 函数中来提供 JSON 数据类型提供的核心索引操作。提取的值被引用以确保结果始终为 JSON 字符串值。

版本 1.3 中的新内容。

成员

init()

类签名

sqlalchemy.dialects.sqlite.JSON (sqlalchemy.types.JSON)

method __init__(none_as_null: bool = False)

继承自 JSONsqlalchemy.types.JSON.__init__ 方法

构造一个 JSON 类型。

参数:

none_as_null=False

如果为 True,则将值 None 持久化为 SQL NULL 值,而不是 null 的 JSON 编码。请注意,当此标志为 False 时,仍然可以使用 null() 构造来持久化 NULL 值,该构造可以直接作为参数值传递,由 JSON 类型特殊解释为 SQL NULL:

from sqlalchemy import null
conn.execute(table.insert(), {"data": null()})

注意

JSON.none_as_null 不适用于传递给 Column.defaultColumn.server_default 的值;这些参数传递的None值表示“无默认值”。

此外,当在 SQL 比较表达式中使用时,Python 值 None 仍然表示 SQL null,而不是 JSON NULL。 JSON.none_as_null 标志明确指的是值在 INSERT 或 UPDATE 语句中的持久性。应使用 JSON.NULL 值进行希望与 JSON null 进行比较的 SQL 表达式。

另请参阅

JSON.NULL

class sqlalchemy.dialects.sqlite.TIME

以字符串形式在 SQLite 中表示 Python 时间对象。

默认的字符串存储格式是:

"%(hour)02d:%(minute)02d:%(second)02d.%(microsecond)06d"

例如:

12:05:57.10558

默认情况下,传入的存储格式是使用 Python time.fromisoformat()函数解析的。

在 2.0 版本中更改:默认时间字符串解析使用 time.fromisoformat()

存储格式可以在一定程度上使用 storage_formatregexp 参数进行自定义,例如:

import re
from sqlalchemy.dialects.sqlite import TIME

t = TIME(storage_format="%(hour)02d-%(minute)02d-"
                        "%(second)02d-%(microsecond)06d",
         regexp=re.compile("(\d+)-(\d+)-(\d+)-(?:-(\d+))?")
)

参数:

  • storage_format – 将应用于带有小时、分钟、秒和微秒键的字典的格式字符串。

  • regexp – 将应用于传入结果行的正则表达式,替换使用 datetime.fromisoformat() 解析传入字符串的用法。如果正则表达式包含命名分组,则生成的匹配字典将作为关键字参数应用于 Python 的 time() 构造函数。否则,如果使用了位置分组,则通过 *map(int, match_obj.groups(0)) 将调用时间()构造函数以位置参数的方式。

类签名

sqlalchemy.dialects.sqlite.TIME (sqlalchemy.dialects.sqlite.base._DateTimeMixin, sqlalchemy.types.Time)

SQLite DML Constructs

对象名称 描述
insert(table) 构造 SQLite 特定的变体 Insert 构造。
Insert INSERT 的 SQLite 特定实现。
function sqlalchemy.dialects.sqlite.insert(table: _DMLTableArgument) → Insert

构造 SQLite 特定的变体 Insert 构造。

sqlalchemy.dialects.sqlite.insert() 函数创建一个 sqlalchemy.dialects.sqlite.Insert。此类基于方言不可知的 Insert 构造,可以使用 SQLAlchemy Core 中的 insert() 函数构造。

Insert 构造包括其他方法 Insert.on_conflict_do_update(), Insert.on_conflict_do_nothing()

class sqlalchemy.dialects.sqlite.Insert

SQLite 特定的 INSERT 实现。

添加了针对 SQLite 特定语法的方法,例如 ON CONFLICT。

使用 sqlalchemy.dialects.sqlite.insert() 函数创建 Insert 对象。

新版本 1.4 中新增。

另请参见

INSERT…ON CONFLICT (Upsert)

成员

excluded, inherit_cache, on_conflict_do_nothing(), on_conflict_do_update()

类签名

sqlalchemy.dialects.sqlite.Insert (sqlalchemy.sql.expression.Insert)

attribute excluded

对于 ON CONFLICT 语句提供了 excluded 命名空间

SQLite 的 ON CONFLICT 子句允许引用将要插入的行,称为 excluded。此属性提供了此行中的所有列以供引用。

提示

Insert.excluded 属性是 ColumnCollection 的一个实例,提供了与 访问表和列 中描述的 Table.c 集合相同的接口。通过该集合,普通名称可以像属性一样访问(例如 stmt.excluded.some_column),但特殊名称和字典方法名称应使用索引访问,例如 stmt.excluded["column name"]stmt.excluded["values"]。有关更多示例,请参阅 ColumnCollection 的文档字符串。

attribute inherit_cache: bool | None = False

指示此 HasCacheKey 实例是否应使用其直接超类使用的缓存键生成方案。

该属性默认为 None,表示结构尚未考虑是否适合参与缓存;这在功能上等同于将值设置为 False,除了还会发出警告。

如果与此类本地属性而不是其超类有关的属性不会改变与对象相对应的 SQL,则可以将此标志设置为 True

See also

为自定义结构启用缓存支持 - 设置HasCacheKey.inherit_cache 属性的一般指南,用于第三方或用户定义的 SQL 构造。

method on_conflict_do_nothing(index_elements: _OnConflictIndexElementsT = None, index_where: _OnConflictIndexWhereT = None) → Self

指定了 ON CONFLICT 子句的 DO NOTHING 操作。

参数:

  • index_elements – 由字符串列名、Column 对象或其他列表达式对象组成的序列,将用于推断目标索引或唯一约束。

  • index_where – 可用于推断条件目标索引的附加 WHERE 准则。

method on_conflict_do_update(index_elements: _OnConflictIndexElementsT = None, index_where: _OnConflictIndexWhereT = None, set_: _OnConflictSetT = None, where: _OnConflictWhereT = None) → Self

指定了 ON CONFLICT 子句的 DO UPDATE SET 操作。

参数:

  • index_elements – 由字符串列名、Column 对象或其他列表达式对象组成的序列,将用于推断目标索引或唯一约束。

  • index_where – 可用于推断条件目标索引的附加 WHERE 准则。

  • set_

    一个字典或其他映射对象,其中键可以是目标表中的列名,或者是 Column 对象或其他 ORM 映射的列,与目标表匹配,以及表达式或字面值作为值,指定要执行的 SET 操作。

    从版本 1.4 开始:Insert.on_conflict_do_update.set_ 参数支持来自目标 TableColumn 对象作为键。

    警告

    这个字典考虑 Python 指定的默认 UPDATE 值或生成函数,例如使用 Column.onupdate 指定的那些。这些值不会对 ON CONFLICT 风格的 UPDATE 生效,除非它们在 Insert.on_conflict_do_update.set_ 字典中手动指定。

  • where – 可选参数。如果存在,则可以是一个 SQL 字符串字面量或 WHERE 子句的可接受表达式,该子句限制了由 DO UPDATE SET 受影响的行。不符合 WHERE 条件的行将不会更新(对于这些行实际上是 DO NOTHING)。

Pysqlite

通过 pysqlite 驱动程序支持 SQLite 数据库。

请注意,pysqlite 与 Python 发行版中包含的 sqlite3 模块是相同的驱动程序。

DBAPI

pysqlite 的文档和下载信息(如果适用)可在此处获取:docs.python.org/library/sqlite3.html

连接

连接字符串:

sqlite+pysqlite:///file_path

驱动程序

sqlite3 Python DBAPI 是所有现代 Python 版本的标准;对于 cPython 和 Pypy,不需要额外安装。

连接字符串

对于 SQLite 数据库的文件规范被视为 URL 的“数据库”部分。请注意,SQLAlchemy URL 的格式是:

driver://user:pass@host/database

文件名的实际使用要从第三个斜杠的右边开始的字符开始。因此,连接到相对文件路径看起来像是:

# relative path
e = create_engine('sqlite:///path/to/database.db')

绝对路径,以斜杠开头表示,意味着你需要四个斜杠:

# absolute path
e = create_engine('sqlite:////path/to/database.db')

要使用 Windows 路径,可以使用常规的驱动器规范和反斜杠。可能需要双反斜杠:

# absolute path on Windows
e = create_engine('sqlite:///C:\\path\\to\\database.db')

要使用 sqlite :memory: 数据库,请将其指定为使用 sqlite://:memory: 的文件名。如果没有路径名,指定 sqlite:// 并什么都不写,也是默认情况:

# in-memory database
e = create_engine('sqlite://:memory:')
# also in-memory database
e2 = create_engine('sqlite://')

URI 连接

现代版本的 SQLite 支持使用驱动程序级 URI进行连接的替代系统,其优势在于可以传递额外的驱动程序级参数,包括“只读”等选项。Python sqlite3 驱动程序在现代 Python 3 版本下支持此模式。SQLAlchemy pysqlite 驱动程序通过在 URL 查询字符串中指定“uri=true”来支持此使用模式。SQLite 级别的“URI”保留为 SQLAlchemy URL 的“数据库”部分(即在斜杠后面):

e = create_engine("sqlite:///file:path/to/database?mode=ro&uri=true")

注意

“uri=true”参数必须出现在 URL 的查询字符串中。如果它仅出现在create_engine.connect_args参数字典中,则目前不会按预期工作。

该逻辑通过分离属于 Python sqlite3 驱动程序的参数和属于 SQLite URI 的参数来协调 SQLAlchemy 的查询字符串和 SQLite 的查询字符串的同时存在。这是通过使用已知被 Python 驱动程序接受的一组固定参数来实现的。例如,要包含指示 Python sqlite3“timeout”和“check_same_thread”参数以及 SQLite“mode”和“nolock”参数的 URL,它们可以一起传递在查询字符串中:

e = create_engine(
    "sqlite:///file:path/to/database?"
    "check_same_thread=true&timeout=10&mode=ro&nolock=1&uri=true"
)

在上面,pysqlite / sqlite3 DBAPI 将传递参数如下:

sqlite3.connect(
    "file:path/to/database?mode=ro&nolock=1",
    check_same_thread=True, timeout=10, uri=True
)

关于将来添加到 Python 或本机驱动程序的参数。应该自动适应此方案的 SQLite URI 方案中添加的新参数名称。添加到 Python 驱动程序端的新参数名称可以通过在create_engine.connect_args字典中指定它们来适应,直到 SQLAlchemy 添加方言支持。对于较不可能的情况,即本机 SQLite 驱动程序添加了一个与现有已知 Python 驱动程序参数之一重叠的新参数名称(例如“timeout”),SQLAlchemy 的方言将需要调整 URL 方案以继续支持这一点。

对于所有 SQLAlchemy 方言,始终可以通过使用create_engine()中的create_engine.creator参数来绕过整个“URL”过程,该参数允许创建一个直接创建 Python sqlite3 驱动程序级连接的自定义可调用对象。

版本 1.3.9 中的新功能。

另请参阅

统一资源标识符 - SQLite 文档中的内容 ### 正则表达式支持

版本 1.4 中的新功能。

支持使用 Python 的re.search函数提供ColumnOperators.regexp_match()操作符。SQLite 本身不包括有效的正则表达式操作符;相反,它包括一个未实现的占位符操作符REGEXP,调用必须提供的用户定义函数。

SQLAlchemy 的实现利用 pysqlite 的create_function钩子如下:

def regexp(a, b):
    return re.search(a, b) is not None

sqlite_connection.create_function(
    "regexp", 2, regexp,
)

目前不支持将正则表达式标志作为单独参数,因为 SQLite 的 REGEXP 操作符不支持这些标志,但可以在正则表达式字符串内联包含这些标志。有关详细信息,请参阅Python 正则表达式

另请参见

Python 正则表达式:Python 正则表达式语法的文档。

与 sqlite3“本地”日期和日期时间类型兼容

pysqlite 驱动程序包括 sqlite3.PARSE_DECLTYPES 和 sqlite3.PARSE_COLNAMES 选项,其效果是任何显式转换为“date”或“timestamp”的列或表达式将转换为 Python 日期或日期时间对象。pysqlite 方言提供的日期和日期时间类型目前与这些选项不兼容,因为它们呈现包括微秒的 ISO 日期/日期时间,而 pysqlite 的驱动程序不包括。此外,SQLAlchemy 目前不会自动呈现“cast”语法,以便使独立函数“current_timestamp”和“current_date”返回本地的日期时间/日期类型。不幸的是,pysqlite 不会在cursor.description中提供标准的 DBAPI 类型,使得 SQLAlchemy 无法在不进行昂贵的每行类型检查的情况下动态检测这些类型。

请记住,不建议使用 pysqlite 的解析选项,也不应该与 SQLAlchemy 一起使用,如果在 create_engine()上配置“native_datetime=True”,则可以强制使用 PARSE_DECLTYPES:

engine = create_engine('sqlite://',
    connect_args={'detect_types':
        sqlite3.PARSE_DECLTYPES|sqlite3.PARSE_COLNAMES},
    native_datetime=True
)

启用此标志后,DATE 和 TIMESTAMP 类型(但请注意 - 不包括 DATETIME 或 TIME 类型...搞糊涂了吗?)将不执行任何绑定参数或结果处理。执行“func.current_date()”将返回一个字符串。“func.current_timestamp()”在 SQLAlchemy 中注册为返回 DATETIME 类型,因此此函数仍然接收 SQLAlchemy 级别的结果处理。

线程/池行为

默认情况下,sqlite3 DBAPI 禁止在创建它的线程之外的线程中使用特定连接。随着 SQLite 的成熟,它在多线程下的行为已经改进,甚至包括选项,允许在多个线程中使用仅内存数据库。

线程禁止被称为“检查同一线程”,可以使用sqlite3参数check_same_thread来控制,该参数将禁用或启用此检查。 SQLAlchemy 在这里的默认行为是,当使用基于文件的数据库时,自动将check_same_thread设置为False,以与默认的池类QueuePool建立兼容性。

SQLAlchemy 的pysqlite DBAPI 基于请求的 SQLite 数据库类型以不同的方式建立连接池:

  • 当指定了一个:memory:的 SQLite 数据库时,默认情况下方言将使用SingletonThreadPool。该池每个线程维护一个单一连接,因此当前线程内对引擎的所有访问都使用相同的:memory:数据库,而其他线程将访问不同的:memory:数据库。check_same_thread参数的默认值为True

  • 当指定文件型数据库时,方言将同时使用QueuePool作为连接源。同时,默认情况下将check_same_thread标志设置为False,除非被覆盖。

    从版本 2.0 开始更改:SQLite 文件数据库引擎现在默认使用QueuePool。先前使用的是NullPool。可以通过在create_engine.poolclass参数中指定NullPool类来使用NullPool类。

为文件数据库禁用连接池

可以通过为poolclass()参数指定NullPool实现来禁用文件型数据库的连接池:

from sqlalchemy import NullPool
engine = create_engine("sqlite:///myfile.db", poolclass=NullPool)

已观察到由于QueuePool没有连接重用,因此NullPool实现在重复检出时产生极小的性能开销。然而,如果应用程序出现文件被锁定的问题,仍然可能有利于使用此类。

在多个线程中使用内存数据库

要在多线程情况下使用 :memory: 数据库,必须共享相同的连接对象,因为数据库仅存在于该连接的范围内。 StaticPool 实现将在全局维护单个连接,并且可以将 check_same_thread 标志传递给 Pysqlite 为 False

from sqlalchemy.pool import StaticPool
engine = create_engine('sqlite://',
                    connect_args={'check_same_thread':False},
                    poolclass=StaticPool)

注意在多线程中使用 :memory: 数据库需要 SQLite 的最新版本。

使用临时表与 SQLite

由于 SQLite 处理临时表的方式,如果希望在基于文件的 SQLite 数据库中跨多个连接池检出时使用临时表,例如在使用 ORM Session 时,临时表应在 Session.commit()Session.rollback() 调用后继续存在,必须使用维护单个连接的连接池。如果作用域仅在当前线程中需要,请使用 SingletonThreadPool,或者如果需要在多个线程中使用,则使用 StaticPool

# maintain the same connection per thread
from sqlalchemy.pool import SingletonThreadPool
engine = create_engine('sqlite:///mydb.db',
                    poolclass=SingletonThreadPool)

# maintain the same connection across all threads
from sqlalchemy.pool import StaticPool
engine = create_engine('sqlite:///mydb.db',
                    poolclass=StaticPool)

请注意,SingletonThreadPool 应配置为要使用的线程数;超出该数量时,连接将以不确定的方式关闭。

处理混合字符串 / 二进制列

SQLite 数据库是弱类型的,因此在使用二进制值时(在 Python 中表示为 b'some string'),可能会出现特定的 SQLite 数据库,其中一些行的数据值将由 Pysqlite 驱动程序返回为 b'' 值,而其他行将作为 Python 字符串返回,例如 '' 值。如果一致使用 SQLAlchemy LargeBinary 数据类型,则不会发生此情况,但是如果特定的 SQLite 数据库具有使用 Pysqlite 驱动程序直接插入的数据,或者当使用 SQLAlchemy String 类型时后来更改为 LargeBinary,表将无法一致可读,因为 SQLAlchemy 的 LargeBinary 数据类型不处理字符串,因此无法对处于字符串格式的值进行“编码”。

要处理具有混合字符串/二进制数据的 SQLite 表中的情况,请使用一个自定义类型,将逐行检查每一行:

from sqlalchemy import String
from sqlalchemy import TypeDecorator

class MixedBinary(TypeDecorator):
    impl = String
    cache_ok = True

    def process_result_value(self, value, dialect):
        if isinstance(value, str):
            value = bytes(value, 'utf-8')
        elif value is not None:
            value = bytes(value)

        return value

然后在通常会使用 LargeBinary 的地方使用上述 MixedBinary 数据类型。

可序列化隔离 / 保存点 / 事务 DDL

在 数据库锁定行为 / 并发性 部分中,我们提到 pysqlite 驱动程序的一系列问题,这些问题会导致 SQLite 的几个功能无法正常工作。 pysqlite DBAPI 驱动程序有几个长期存在的错误,影响其事务行为的正确性。 在其默认操作模式下,SQLite 的功能,如 SERIALIZABLE 隔离、事务 DDL 和 SAVEPOINT 支持是不起作用的,为了使用这些功能,必须采取解决方法。

问题本质上是驱动程序试图猜测用户的意图,未能启动事务,有时会过早结束事务,以减少 SQLite 数据库的文件锁定行为,尽管 SQLite 本身对只读活动使用“共享”锁。

SQLAlchemy 默认选择不更改此行为,因为这是 pysqlite 驱动程序长期期望的行为;如果 pysqlite 驱动程序尝试修复这些问题,那将更多地影响到 SQLAlchemy 的默认设置。

好消息是,通过几个事件,我们可以完全实现事务支持,通过完全禁用 pysqlite 的功能并自己发出 BEGIN 来实现。 这是通过使用两个事件监听器实现的:

from sqlalchemy import create_engine, event

engine = create_engine("sqlite:///myfile.db")

@event.listens_for(engine, "connect")
def do_connect(dbapi_connection, connection_record):
    # disable pysqlite's emitting of the BEGIN statement entirely.
    # also stops it from emitting COMMIT before any DDL.
    dbapi_connection.isolation_level = None

@event.listens_for(engine, "begin")
def do_begin(conn):
    # emit our own BEGIN
    conn.exec_driver_sql("BEGIN")

警告

在使用上述方法时,建议不要在 SQLite 驱动程序上使用 Connection.execution_options.isolation_level 设置,因为这个函数必然也会改变“.isolation_level”设置。

在上面,我们拦截一个新的 pysqlite 连接并禁用任何事务集成。然后,在 SQLAlchemy 知道事务范围即将开始的时候,我们自己发出 "BEGIN"

当我们控制 "BEGIN" 时,我们也可以直接控制 SQLite 的锁定模式,通过在我们的 "BEGIN" 中添加所需的锁定模式:

@event.listens_for(engine, "begin")
def do_begin(conn):
    conn.exec_driver_sql("BEGIN EXCLUSIVE")

另请参阅

BEGIN TRANSACTION - 在 SQLite 网站上

sqlite3 SELECT 不会开始事务 - 在 Python 缺陷跟踪器上

sqlite3 模块破坏事务并可能损坏数据 - 在 Python 缺陷跟踪器上 ### 用户定义函数

pysqlite 支持一个create_function()方法,允许我们在 Python 中创建自己的用户定义函数(UDF)并直接在 SQLite 查询中使用它们。这些函数与特定的 DBAPI 连接相关联。

SQLAlchemy 使用基于文件的 SQLite 数据库的连接池,因此我们需要确保在创建连接时将 UDF 附加到连接上。通过事件监听器实现:

from sqlalchemy import create_engine
from sqlalchemy import event
from sqlalchemy import text

def udf():
    return "udf-ok"

engine = create_engine("sqlite:///./db_file")

@event.listens_for(engine, "connect")
def connect(conn, rec):
    conn.create_function("udf", 0, udf)

for i in range(5):
    with engine.connect() as conn:
        print(conn.scalar(text("SELECT UDF()")))

数据库 API

pysqlite 的文档和下载信息(如果适用)可在此处获取:docs.python.org/library/sqlite3.html

连接

连接字符串:

sqlite+pysqlite:///file_path

驱动程序

在所有现代 Python 版本上,sqlite3 Python 数据库 API 是标准的;对于 cPython 和 Pypy,不需要额外安装。

连接字符串

SQLite 数据库的文件规范被视为 URL 的“数据库”部分。请注意,SQLAlchemy URL 的格式是:

driver://user:pass@host/database

这意味着要使用的实际文件名从第三个斜杠的右侧字符开始。因此,连接到相对文件路径看起来像:

# relative path
e = create_engine('sqlite:///path/to/database.db')

绝对路径,以斜杠开头,意味着你需要四个斜杠:

# absolute path
e = create_engine('sqlite:////path/to/database.db')

要使用 Windows 路径,可以使用常规的驱动器规范和反斜杠。可能需要双反斜杠:

# absolute path on Windows
e = create_engine('sqlite:///C:\\path\\to\\database.db')

要使用 sqlite 的:memory:数据库,请将其指定为文件名,使用sqlite://:memory:。如果没有文件路径,则它也是默认值,只需指定sqlite://而不加其他内容:

# in-memory database
e = create_engine('sqlite://:memory:')
# also in-memory database
e2 = create_engine('sqlite://')

URI 连接

现代版本的 SQLite 支持使用驱动程序级 URI进行连接的替代系统,其优点是可以传递额外的驱动程序级参数,包括“只读”选项。 Python sqlite3 驱动程序在现代 Python 3 版本下支持此模式。 SQLAlchemy pysqlite 驱动程序通过在 URL 查询字符串中指定“uri=true”来支持此使用模式。 SQLite 级别的“URI”保留为 SQLAlchemy URL 的“数据库”部分(即,跟在斜杠后面):

e = create_engine("sqlite:///file:path/to/database?mode=ro&uri=true")

注意

“uri=true”参数必须出现在 URL 的查询字符串中。如果它只出现在create_engine.connect_args参数字典中,则当前不会按预期工作。

该逻辑通过分离属于 Python sqlite3 驱动程序与属于 SQLite URI 的参数,来调和 SQLAlchemy 查询字符串和 SQLite 查询字符串的同时出现。这通过使用一个已知被驱动程序的 Python 部分接受的固定参数列表来实现。例如,要包含指示 Python sqlite3“timeout”和“check_same_thread”参数以及 SQLite“mode”和“nolock”参数的 URL,它们可以一起传递到查询字符串中:

e = create_engine(
    "sqlite:///file:path/to/database?"
    "check_same_thread=true&timeout=10&mode=ro&nolock=1&uri=true"
)

上面,pysqlite / sqlite3 DBAPI 将被传递参数如下:

sqlite3.connect(
    "file:path/to/database?mode=ro&nolock=1",
    check_same_thread=True, timeout=10, uri=True
)

关于将来添加到 Python 或本机驱动程序的新参数。添加到 SQLite URI 方案的新参数名称应该自动适应此方案。添加到 Python 驱动程序端的新参数名称可以通过在create_engine.connect_args字典中指定它们来适应,直到 SQLAlchemy 添加了方言支持。对于本机 SQLite 驱动程序添加一个与现有已知 Python 驱动程序参数(例如“timeout”)重叠的新参数名称的可能性较小,SQLAlchemy 的方言将需要调整 URL 方案以继续支持此参数。

对于所有 SQLAlchemy 方言,始终可以通过使用create_engine.creator参数绕过整个“URL”过程,在create_engine()中直接通过使用一个自定义可调用对象来创建 Python sqlite3 驱动程序级别的连接。

版本 1.3.9 中的新功能。

另请参见

统一资源标识符 - 在 SQLite 文档中 #### URI 连接

现代版本的 SQLite 支持使用驱动级 URI进行连接的替代系统,其优势在于可以传递额外的驱动级参数,包括“只读”等选项。Python sqlite3 驱动程序在现代 Python 3 版本下支持此模式。SQLAlchemy pysqlite 驱动程序通过在 URL 查询字符串中指定“uri=true”来支持此使用模式。SQLite 级别的“URI”保留为 SQLAlchemy URL 的“database”部分(即,在斜杠后面):

e = create_engine("sqlite:///file:path/to/database?mode=ro&uri=true")

注意

“uri=true” 参数必须出现在 URL 的查询字符串中。如果它只存在于create_engine.connect_args参数字典中,它目前不会按预期工作。

逻辑通过将属于 Python sqlite3 驱动程序的参数与属于 SQLite URI 的参数分开,来协调 SQLAlchemy 的查询字符串和 SQLite 的查询字符串的同时存在。这是通过使用已知被 Python 驱动程序接受的一组固定参数来实现的。例如,要包含指示 Python sqlite3“timeout”和“check_same_thread”参数以及 SQLite“mode”和“nolock”参数的 URL,它们可以一起传递在查询字符串中:

e = create_engine(
    "sqlite:///file:path/to/database?"
    "check_same_thread=true&timeout=10&mode=ro&nolock=1&uri=true"
)

上面,pysqlite / sqlite3 DBAPI 将被传递参数如下:

sqlite3.connect(
    "file:path/to/database?mode=ro&nolock=1",
    check_same_thread=True, timeout=10, uri=True
)

关于将来添加到 Python 或本机驱动程序的参数。新增加到 SQLite URI 方案的参数名应该由该方案自动适应。新增加到 Python 驱动程序端的参数名可以通过在 create_engine.connect_args 字典中指定它们来容纳,直到 SQLAlchemy 添加了方言支持。对于较不可能的情况,即本机 SQLite 驱动程序添加了与现有已知 Python 驱动程序参数(例如“timeout”)重叠的新参数名,SQLAlchemy 的方言需要调整 URL 方案以继续支持此参数。

与 SQLAlchemy 方言的所有情况一样,整个“URL”过程都可以通过 create_engine() 中的 create_engine.creator 参数绕过,该参数允许自定义可调用项,直接创建 Python sqlite3 驱动程序级连接。

1.3.9 版的新内容。

另请参阅

统一资源标识符 - SQLite 文档中

正则表达式支持

1.4 版中的新内容。

支持使用 Python 的 re.search 函数提供 ColumnOperators.regexp_match() 操作符。SQLite 本身不包括工作正则表达式运算符;相反,它包括一个未实现的占位符操作符 REGEXP,该操作符调用必须提供的用户定义函数。

SQLAlchemy 的实现使用 pysqlite create_function 钩子,如下所示:

def regexp(a, b):
    return re.search(a, b) is not None

sqlite_connection.create_function(
    "regexp", 2, regexp,
)

目前不支持将正则表达式标志作为单独参数,因为这些标志不受 SQLite 的 REGEXP 操作符支持,但可以内联在正则表达式字符串中。有关详情,请参阅 Python 正则表达式

另请参阅

Python 正则表达式:Python 正则表达式语法的文档。

兼容性与 sqlite3 的“本地”日期和日期时间类型

pysqlite 驱动程序包括 sqlite3.PARSE_DECLTYPES 和 sqlite3.PARSE_COLNAMES 选项,其效果是任何明确转换为“date”或“timestamp”的列或表达式将被转换为 Python 的日期或日期时间对象。pysqlite 方言提供的日期和日期时间类型目前与这些选项不兼容,因为它们呈现的 ISO 日期/日期时间包括微秒,而 pysqlite 的驱动程序没有。此外,SQLAlchemy 目前不会自动渲染“cast”语法,该语法要求独立的函数“current_timestamp”和“current_date”以本地返回 datetime/date 类型。不幸的是,pysqlite 不会在 cursor.description 中提供标准的 DBAPI 类型,因此 SQLAlchemy 无法在不执行昂贵的每行类型检查的情况下即时检测到这些类型。

特别注意,pysqlite 的解析选项不建议使用,也不应该在与 SQLAlchemy 一起使用时需要使用,如果在 create_engine() 上配置了 “native_datetime=True”,则可以强制使用 PARSE_DECLTYPES 选项:

engine = create_engine('sqlite://',
    connect_args={'detect_types':
        sqlite3.PARSE_DECLTYPES|sqlite3.PARSE_COLNAMES},
    native_datetime=True
)

启用此标志后,DATE 和 TIMESTAMP 类型(但请注意 - 不是 DATETIME 或 TIME 类型…搞糊涂了吗?)将不执行任何绑定参数或结果处理。执行“func.current_date()”将返回一个字符串。“func.current_timestamp()”在 SQLAlchemy 中注册为返回 DATETIME 类型,因此此函数仍然接收 SQLAlchemy 级别的结果处理。

线程/池行为

默认情况下,sqlite3 DBAPI 禁止在非创建它的线程中使用特定的连接。随着 SQLite 的成熟,它在多线程下的行为已经改进,甚至包括选项让内存数据库可以在多个线程中使用。

线程禁止被称为“检查同一线程”,可以使用 sqlite3 参数 check_same_thread 来控制,这将禁用或启用此检查。SQLAlchemy 在这里的默认行为是,当使用基于文件的数据库时,自动将 check_same_thread 设置为 False,以确保与默认的池类 QueuePool 兼容。

SQLAlchemy pysqlite DBAPI 根据所请求的 SQLite 数据库的类型不同而建立连接池:

  • 当指定了一个 :memory: 的 SQLite 数据库时,默认情况下方言会使用 SingletonThreadPool。这个池每个线程维护一个连接,所以当前线程内的对引擎的所有访问都使用同一个 :memory: 数据库 - 其他线程将访问一个不同的 :memory: 数据库。check_same_thread 参数默认为 True

  • 当指定基于文件的数据库时,方言将使用QueuePool作为连接的源。同时,默认情况下将check_same_thread标志设置为 False,除非被覆盖。

    从版本 2.0 开始更改:SQLite 文件数据库引擎现在默认使用QueuePool。以前使用的是NullPool。可以通过create_engine.poolclass参数指定使用NullPool类。

禁用文件数据库的连接池

通过为poolclass()参数指定NullPool实现,可以禁用基于文件的数据库的连接池:

from sqlalchemy import NullPool
engine = create_engine("sqlite:///myfile.db", poolclass=NullPool)

据观察,NullPool实现由于QueuePool实现的连接不重用而导致极小的性能开销。但是,如果应用程序遇到文件被锁定的问题,仍然可能有益于使用此类。

在多个线程中使用内存数据库

在多线程场景中使用:memory:数据库,必须共享相同的连接对象,因为数据库仅存在于该连接的范围内。StaticPool实现将全局维护一个单一连接,并且check_same_thread标志可以传递给 Pysqlite 作为 False

from sqlalchemy.pool import StaticPool
engine = create_engine('sqlite://',
                    connect_args={'check_same_thread':False},
                    poolclass=StaticPool)

请注意,在多线程中使用:memory:数据库需要最新版本的 SQLite。

使用 SQLite 临时表

由于 SQLite 处理临时表的方式,如果希望在基于文件的 SQLite 数据库中跨多个连接池检出使用临时表,例如在使用 ORM Session时,临时表应在Session.commit()Session.rollback()之后继续保留,必须使用维护单个连接的池。如果范围仅在当前线程内使用,则使用SingletonThreadPool,或者在此情况下需要在多个线程中使用范围,则使用StaticPool

# maintain the same connection per thread
from sqlalchemy.pool import SingletonThreadPool
engine = create_engine('sqlite:///mydb.db',
                    poolclass=SingletonThreadPool)

# maintain the same connection across all threads
from sqlalchemy.pool import StaticPool
engine = create_engine('sqlite:///mydb.db',
                    poolclass=StaticPool)

请注意,应该为SingletonThreadPool配置要使用的线程数;超出该数量,连接将以不确定的方式关闭。

禁用文件数据库的连接池

可以通过为poolclass()参数指定NullPool实现来禁用基于文件的数据库的池化:

from sqlalchemy import NullPool
engine = create_engine("sqlite:///myfile.db", poolclass=NullPool)

使用NullPool实现观察到,由于QueuePool没有实现连接重用,因此对于重复检出,它会产生极小的性能开销。然而,如果应用程序遇到文件被锁定的问题,仍然可能有利用这个类。

在多线程中使用内存数据库

在多线程方案中使用:memory:数据库,相同的连接对象必须在线程之间共享,因为数据库仅存在于该连接的范围内。StaticPool实现将在全局维护一个单一连接,并且check_same_thread标志可以传递给 Pysqlite,设置为False

from sqlalchemy.pool import StaticPool
engine = create_engine('sqlite://',
                    connect_args={'check_same_thread':False},
                    poolclass=StaticPool)

请注意,在多个线程中使用:memory:数据库需要 SQLite 的最新版本。

使用 SQLite 临时表

由于 SQLite 处理临时表的方式,如果希望在基于文件的 SQLite 数据库中跨多次从连接池检出时使用临时表,例如在使用 ORM Session时,在Session.commit()Session.rollback()之后,临时表应继续保持,必须使用维护单个连接的池。如果范围仅在当前线程内需要,则使用SingletonThreadPool,如果在多个线程中需要范围,则使用StaticPool用于此案例:

# maintain the same connection per thread
from sqlalchemy.pool import SingletonThreadPool
engine = create_engine('sqlite:///mydb.db',
                    poolclass=SingletonThreadPool)

# maintain the same connection across all threads
from sqlalchemy.pool import StaticPool
engine = create_engine('sqlite:///mydb.db',
                    poolclass=StaticPool)

请注意,SingletonThreadPool应配置为要使用的线程数;超出该数字后,连接将以不确定的方式关闭。

处理混合字符串/二进制列

SQLite 数据库是弱类型的,因此当使用二进制值时,可能出现一种情况,即在 Python 中表示为b'some string'的情况下,特定的 SQLite 数据库可能会在不同的行中具有不同的数据值,其中一些将被 Pysqlite 驱动器返回为b''值,而另一些将被返回为 Python 字符串,例如''值。如果一直使用 SQLAlchemy 的LargeBinary数据类型,则不会发生此情况,但是如果特定的 SQLite 数据库具有使用 Pysqlite 驱动器直接插入的数据,或者在使用后将其更改为LargeBinary的 SQLAlchemy String类型时,表将无法一致地读取,因为 SQLAlchemy 的LargeBinary数据类型不处理字符串,因此无法“编码”字符串格式的值。

要处理具有相同列中的混合字符串/二进制数据的 SQLite 表,请使用自定义类型逐个检查每一行:

from sqlalchemy import String
from sqlalchemy import TypeDecorator

class MixedBinary(TypeDecorator):
    impl = String
    cache_ok = True

    def process_result_value(self, value, dialect):
        if isinstance(value, str):
            value = bytes(value, 'utf-8')
        elif value is not None:
            value = bytes(value)

        return value

然后在通常会使用LargeBinary的地方使用上述的MixedBinary数据类型。

可序列化隔离/保存点/事务 DDL

在 数据库锁定行为 / 并发性 部分中,我们提到了 pysqlite 驱动程序的各种问题,这些问题阻止了 SQLite 的几个功能正常工作。pysqlite DBAPI 驱动程序有一些长期存在的错误,这些错误影响了其事务行为的正确性。在其默认操作模式下,SQLite 功能(如 SERIALIZABLE 隔离、事务性 DDL 和 SAVEPOINT 支持)是不起作用的,为了使用这些功能,必须采取一些变通方法。

问题本质上是驱动程序试图猜测用户的意图,未能启动事务,并有时过早结束它们,以尽量减少 SQLite 数据库的文件锁定行为,尽管 SQLite 本身对只读活动使用“共享”锁。

SQLAlchemy 选择默认情况下不更改此行为,因为这是 pysqlite 驱动程序的长期期望行为;如果 pysqlite 驱动程序尝试修复这些问题,那将更多地驱动 SQLAlchemy 的默认设置。

好消息是,通过几个事件,我们可以完全实现事务支持,方法是完全禁用 pysqlite 的功能,并自行发出 BEGIN。这通过两个事件监听器实现:

from sqlalchemy import create_engine, event

engine = create_engine("sqlite:///myfile.db")

@event.listens_for(engine, "connect")
def do_connect(dbapi_connection, connection_record):
    # disable pysqlite's emitting of the BEGIN statement entirely.
    # also stops it from emitting COMMIT before any DDL.
    dbapi_connection.isolation_level = None

@event.listens_for(engine, "begin")
def do_begin(conn):
    # emit our own BEGIN
    conn.exec_driver_sql("BEGIN")

警告

当使用上述方法时,建议不要在 SQLite 驱动程序上使用 Connection.execution_options.isolation_level 设置以及 create_engine(),因为这个函数必然会改变“.isolation_level”设置。

上面,我们拦截了一个新的 pysqlite 连接,并禁用了任何事务集成。然后,在 SQLAlchemy 知道事务范围即将开始的时候,我们自己发出了 "BEGIN"

当我们控制 "BEGIN" 时,我们也可以直接控制 SQLite 的锁定模式,通过在我们的 "BEGIN" 中添加所需的锁定模式来引入 BEGIN TRANSACTION 中的锁定模式:

@event.listens_for(engine, "begin")
def do_begin(conn):
    conn.exec_driver_sql("BEGIN EXCLUSIVE")

另请参阅

BEGIN TRANSACTION - SQLite 网站上的内容

sqlite3 SELECT does not BEGIN a transaction - Python 缺陷跟踪器上的问题

sqlite3 模块破坏事务并可能损坏数据 - Python 缺陷跟踪器上的问题

用户定义的函数

pysqlite 支持一个 create_function() 方法,允许我们在 Python 中创建自己的用户定义函数(UDFs),并直接在 SQLite 查询中使用它们。这些函数与特定的 DBAPI 连接相关联。

SQLAlchemy 在基于文件的 SQLite 数据库中使用连接池,因此我们需要确保在创建连接时将 UDF 附加到连接上。这可以通过事件侦听器完成:

from sqlalchemy import create_engine
from sqlalchemy import event
from sqlalchemy import text

def udf():
    return "udf-ok"

engine = create_engine("sqlite:///./db_file")

@event.listens_for(engine, "connect")
def connect(conn, rec):
    conn.create_function("udf", 0, udf)

for i in range(5):
    with engine.connect() as conn:
        print(conn.scalar(text("SELECT UDF()")))

Aiosqlite

通过 aiosqlite 驱动程序支持 SQLite 数据库。

DBAPI

aiosqlite 的文档和下载信息 (如果适用) 可在此处找到: pypi.org/project/aiosqlite/

连接

连接字符串:

sqlite+aiosqlite:///file_path

aiosqlite 方言提供了对在 pysqlite 上运行的 SQLAlchemy asyncio 接口的支持。

aiosqlite 是 pysqlite 的一个封装,它为每个连接使用一个后台线程。它实际上不使用非阻塞 IO,因为 SQLite 数据库不是基于套接字的。但是,它提供了一个有效的 asyncio 接口,对于测试和原型设计非常有用。

使用特殊的 asyncio 中介层,aiosqlite 方言可用作 SQLAlchemy asyncio 扩展包的后端。

这个方言通常应该仅与 create_async_engine() 引擎创建函数一起使用:

from sqlalchemy.ext.asyncio import create_async_engine
engine = create_async_engine("sqlite+aiosqlite:///filename")

URL 通过所有参数传递给 pysqlite 驱动程序,因此所有连接参数与 Pysqlite 的参数相同。

用户定义函数

aiosqlite 扩展了 pysqlite 来支持异步,因此我们可以在 Python 中创建自己的用户定义函数 (UDFs),并直接在 SQLite 查询中使用它们,如此处所述: 用户定义函数。### 可串行化隔离/保存点/事务 DDL (asyncio 版本)

与 pysqlite 类似,aiosqlite 不支持 SAVEPOINT 功能。

解决方案类似于 可串行化隔离/保存点/事务 DDL。这通过 async 中的事件侦听器实现:

from sqlalchemy import create_engine, event
from sqlalchemy.ext.asyncio import create_async_engine

engine = create_async_engine("sqlite+aiosqlite:///myfile.db")

@event.listens_for(engine.sync_engine, "connect")
def do_connect(dbapi_connection, connection_record):
    # disable aiosqlite's emitting of the BEGIN statement entirely.
    # also stops it from emitting COMMIT before any DDL.
    dbapi_connection.isolation_level = None

@event.listens_for(engine.sync_engine, "begin")
def do_begin(conn):
    # emit our own BEGIN
    conn.exec_driver_sql("BEGIN")

警告

使用上述方法时,建议不要在 SQLite 驱动程序上使用 Connection.execution_options.isolation_level 设置,以及不要在 Connectioncreate_engine() 上使用,因为这个函数必然也会改变“isolation_level”设置。

DBAPI

aiosqlite 的文档和下载信息 (如果适用) 可在此处找到: pypi.org/project/aiosqlite/

连接

连接字符串:

sqlite+aiosqlite:///file_path

用户定义函数

aiosqlite 扩展了 pysqlite 来支持异步,因此我们可以在 Python 中创建自己的用户定义函数 (UDFs),并直接在 SQLite 查询中使用它们,如此处所述: 用户定义函数。

Serializable isolation / Savepoints / Transactional DDL(asyncio 版本)

与 pysqlite 类似,aiosqlite 不支持 SAVEPOINT 功能。

解决方案类似于 Serializable isolation / Savepoints / Transactional DDL。这是通过异步事件监听器实现的:

from sqlalchemy import create_engine, event
from sqlalchemy.ext.asyncio import create_async_engine

engine = create_async_engine("sqlite+aiosqlite:///myfile.db")

@event.listens_for(engine.sync_engine, "connect")
def do_connect(dbapi_connection, connection_record):
    # disable aiosqlite's emitting of the BEGIN statement entirely.
    # also stops it from emitting COMMIT before any DDL.
    dbapi_connection.isolation_level = None

@event.listens_for(engine.sync_engine, "begin")
def do_begin(conn):
    # emit our own BEGIN
    conn.exec_driver_sql("BEGIN")

警告

当使用上述配方时,建议不要在 SQLite 驱动上使用Connection.execution_options.isolation_level设置,并且不要在Connectioncreate_engine()中使用,因为这个函数必然会改变“.isolation_level”设置。

Pysqlcipher

通过 pysqlcipher 驱动支持 SQLite 数据库。

支持使用 SQLCipher 后端的 DBAPI 的方言。

连接

连接字符串:

sqlite+pysqlcipher://:passphrase@/file_path[?kdf_iter=<iter>]

驱动程序

当前的方言选择逻辑是:

警告

pysqlcipher3pysqlcipher DBAPI 驱动已不再维护;截至目前为止,sqlcipher3 驱动似乎是最新的。为了未来的兼容性,可以使用任何与 pysqlcipher 兼容的 DBAPI,如下所示:

import sqlcipher_compatible_driver

from sqlalchemy import create_engine

e = create_engine(
    "sqlite+pysqlcipher://:password@/dbname.db",
    module=sqlcipher_compatible_driver
)

这些驱动程序使用了 SQLCipher 引擎。该系统基本上引入了新的 PRAGMA 命令到 SQLite,这些命令允许设置密码和其他加密参数,从而允许对数据库文件进行加密。

连接字符串

连接字符串的格式在各方面与pysqlite驱动程序完全相同,只是现在接受“password”字段,其中应包含一个密码:

e = create_engine('sqlite+pysqlcipher://:testing@/foo.db')

对于绝对文件路径,数据库名称应使用两个前导斜杠:

e = create_engine('sqlite+pysqlcipher://:testing@//path/to/foo.db')

可以在查询字符串中传递由 SQLCipher 文档记录的一些额外的与加密相关的 PRAGMA,这将导致每个新连接调用该 PRAGMA。目前支持 cipherkdf_itercipher_page_sizecipher_use_hmac

e = create_engine('sqlite+pysqlcipher://:testing@/foo.db?cipher=aes-256-cfb&kdf_iter=64000')

警告

先前版本的 sqlalchemy 没有考虑到 url 字符串中传递的与加密相关的 pragma,这些 pragma 被静默忽略。如果加密选项不匹配,这可能导致打开先前 sqlalchemy 版本保存的文件时出错。

池行为

该驱动对 pysqlite 的默认池行为进行了更改,如 Threading/Pooling Behavior 所述。观察到 pysqlcipher 驱动在连接方面明显比 pysqlite 驱动慢得多,很可能是由于加密开销,因此此处的方言默认使用 SingletonThreadPool 实现,而不是 pysqlite 使用的 NullPool 池。与以往一样,池实现完全可通过 create_engine.poolclass 参数进行配置;StaticPool 可能更适合单线程使用,或者可以使用 NullPool 来防止未加密的连接被保持打开长时间,但新连接的启动时间会变慢。

连接

连接字符串:

sqlite+pysqlcipher://:passphrase@/file_path[?kdf_iter=<iter>]

驱动程序

当前方言选择逻辑为:

警告

pysqlcipher3pysqlcipher DBAPI 驱动已经不再维护;截至目前,sqlcipher3 驱动似乎是最新的。为了未来的兼容性,可以使用任何与 pysqlcipher 兼容的 DBAPI,如下所示:

import sqlcipher_compatible_driver

from sqlalchemy import create_engine

e = create_engine(
    "sqlite+pysqlcipher://:password@/dbname.db",
    module=sqlcipher_compatible_driver
)

这些驱动程序使用了 SQLCipher 引擎。该系统基本上向 SQLite 引入了新的 PRAGMA 命令,允许设置密码短语和其他加密参数,从而使数据库文件被加密。

连接字符串

连接字符串的格式与pysqlite驱动完全相同,只是现在接受了“password”字段,其中应该包含一个密码短语:

e = create_engine('sqlite+pysqlcipher://:testing@/foo.db')

对于绝对文件路径,应该在数据库名称前使用两个斜杠:

e = create_engine('sqlite+pysqlcipher://:testing@//path/to/foo.db')

可以在查询字符串中传递一组额外的与加密相关的 SQLCipher 所支持的 pragma,详见www.zetetic.net/sqlcipher/sqlcipher-api/,并且会导致每个新连接调用该 PRAGMA。目前支持的有:cipherkdf_itercipher_page_sizecipher_use_hmac

e = create_engine('sqlite+pysqlcipher://:testing@/foo.db?cipher=aes-256-cfb&kdf_iter=64000')

警告

先前版本的 SQLAlchemy 并未考虑传递在 URL 字符串中的与加密相关的 pragma,这些 pragma 被默默忽略。如果加密选项不匹配,这可能导致在打开之前由先前的 SQLAlchemy 版本保存的文件时出现错误。

池行为

驱动程序对 pysqlite 的默认池行为进行了更改,详见线程/池行为。观察到 pysqlcipher 驱动程序在连接时比 pysqlite 驱动程序慢得多,很可能是由于加密开销,因此这里的方言默认使用 SingletonThreadPool 实现,而不是 pysqlite 使用的 NullPool 池。与往常一样,池实现完全可配置,使用 create_engine.poolclass 参数;StaticPool 可能更适合单线程使用,或者 NullPool 可以用于防止未加密的连接长时间保持打开,但会牺牲新连接的启动速度。

posted @ 2024-06-22 11:33  绝不原创的飞龙  阅读(49)  评论(0编辑  收藏  举报