SqlAlchemy 2.0 中文文档(四十九)(3)https://developer.aliyun.com/article/1563029
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 通过 Index
的 mysql_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 都提供了创建带有一定长度的索引条目的选项,其中“长度”指的是将成为索引一部分的每个值中的字符或字节数。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 通过 Index
的 mysql_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”。在ForeignKeyConstraint
或ForeignKey
上使用deferrable
或initially
关键字参数将导致这些关键字在 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”。在 ForeignKeyConstraint
或 ForeignKey
中使用 deferrable
或 initially
关键字参数会使这些关键字在 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=True
的 Index
构造,表示唯一索引,以及表示唯一约束的 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") ) )
使用DateTime
和DATETIME
数据类型也适用相同的说明:
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") ) )
同样的指令适用于使用DateTime
和DATETIME
数据类型的情况:
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
实例。
SqlAlchemy 2.0 中文文档(四十九)(5)https://developer.aliyun.com/article/1563031