SqlAlchemy 2.0 中文文档(三十七)(2)

本文涉及的产品
云原生数据库 PolarDB MySQL 版,通用型 2核4GB 50GB
云原生数据库 PolarDB PostgreSQL 版,标准版 2核4GB 50GB
简介: SqlAlchemy 2.0 中文文档(三十七)

SqlAlchemy 2.0 中文文档(三十七)(1)https://developer.aliyun.com/article/1562696


更多示例

“UTC 时间戳”函数

一个类似于 “CURRENT_TIMESTAMP” 的函数,但应用适当的转换,使时间为 UTC 时间。时间戳最好存储在关系型数据库中作为  UTC,不带时区。UTC 使您的数据库在夏令时结束时不会认为时间已经倒退,不带时区是因为时区就像字符编码 -  最好只在应用程序的端点(即在用户输入时转换为 UTC,在显示时重新应用所需的时区)应用它们。

对于 PostgreSQL 和 Microsoft SQL Server:

from sqlalchemy.sql import expression
from sqlalchemy.ext.compiler import compiles
from sqlalchemy.types import DateTime
class utcnow(expression.FunctionElement):
    type = DateTime()
    inherit_cache = True
@compiles(utcnow, 'postgresql')
def pg_utcnow(element, compiler, **kw):
    return "TIMEZONE('utc', CURRENT_TIMESTAMP)"
@compiles(utcnow, 'mssql')
def ms_utcnow(element, compiler, **kw):
    return "GETUTCDATE()"

示例用法:

from sqlalchemy import (
            Table, Column, Integer, String, DateTime, MetaData
        )
metadata = MetaData()
event = Table("event", metadata,
    Column("id", Integer, primary_key=True),
    Column("description", String(50), nullable=False),
    Column("timestamp", DateTime, server_default=utcnow())
)

“GREATEST”函数

“GREATEST”函数接受任意数量的参数,并返回具有最高值的参数 - 它等同于 Python 的 max 函数。与仅容纳两个参数的基于 CASE 的版本相比,SQL 标准版本:

from sqlalchemy.sql import expression, case
from sqlalchemy.ext.compiler import compiles
from sqlalchemy.types import Numeric
class greatest(expression.FunctionElement):
    type = Numeric()
    name = 'greatest'
    inherit_cache = True
@compiles(greatest)
def default_greatest(element, compiler, **kw):
    return compiler.visit_function(element)
@compiles(greatest, 'sqlite')
@compiles(greatest, 'mssql')
@compiles(greatest, 'oracle')
def case_greatest(element, compiler, **kw):
    arg1, arg2 = list(element.clauses)
    return compiler.process(case((arg1 > arg2, arg1), else_=arg2), **kw)

示例用法:

Session.query(Account).\
        filter(
            greatest(
                Account.checking_balance,
                Account.savings_balance) > 10000
        )

“false” 表达式

渲染“false”常量表达式,对于没有“false”常量的平台,渲染为“0”:

from sqlalchemy.sql import expression
from sqlalchemy.ext.compiler import compiles
class sql_false(expression.ColumnElement):
    inherit_cache = True
@compiles(sql_false)
def default_false(element, compiler, **kw):
    return "false"
@compiles(sql_false, 'mssql')
@compiles(sql_false, 'mysql')
@compiles(sql_false, 'oracle')
def int_false(element, compiler, **kw):
    return "0"

示例用法:

from sqlalchemy import select, union_all
exp = union_all(
    select(users.c.name, sql_false().label("enrolled")),
    select(customers.c.name, customers.c.enrolled)
)
对象名称 描述
compiles(class_, *specs) 为给定ClauseElement类型注册函数作为编译器。
deregister(class_) 删除与给定ClauseElement类型关联的所有自定义编译器。
function sqlalchemy.ext.compiler.compiles(class_, *specs)

为给定ClauseElement类型注册函数作为编译器。

function sqlalchemy.ext.compiler.deregister(class_)

删除与给定ClauseElement类型关联的所有自定义编译器。

概要

使用涉及创建一个或多个ClauseElement子类和一个或多个定义其编译的可调用对象:

from sqlalchemy.ext.compiler import compiles
from sqlalchemy.sql.expression import ColumnClause
class MyColumn(ColumnClause):
    inherit_cache = True
@compiles(MyColumn)
def compile_mycolumn(element, compiler, **kw):
    return "[%s]" % element.name

上面,MyColumn 扩展了ColumnClause,命名列对象的基本表达式元素。compiles 装饰器将自身注册到 MyColumn 类,以便在对象编译为字符串时调用它:

from sqlalchemy import select
s = select(MyColumn('x'), MyColumn('y'))
print(str(s))

产生:

SELECT [x], [y]

特定于方言的编译规则

编译器也可以是特定于方言的。将为使用的方言调用适当的编译器:

from sqlalchemy.schema import DDLElement
class AlterColumn(DDLElement):
    inherit_cache = False
    def __init__(self, column, cmd):
        self.column = column
        self.cmd = cmd
@compiles(AlterColumn)
def visit_alter_column(element, compiler, **kw):
    return "ALTER COLUMN %s ..." % element.column.name
@compiles(AlterColumn, 'postgresql')
def visit_alter_column(element, compiler, **kw):
    return "ALTER TABLE %s ALTER COLUMN %s ..." % (element.table.name,
                                                   element.column.name)

当使用任何 postgresql 方言时,将调用第二个 visit_alter_table

编译自定义表达式结构的子元素

compiler 参数是正在使用的 Compiled 对象。此对象可以用于检查关于正在进行的编译的任何信息,包括 compiler.dialectcompiler.statement 等。SQLCompilerDDLCompiler 都包含一个 process() 方法,可用于编译嵌入属性:

from sqlalchemy.sql.expression import Executable, ClauseElement
class InsertFromSelect(Executable, ClauseElement):
    inherit_cache = False
    def __init__(self, table, select):
        self.table = table
        self.select = select
@compiles(InsertFromSelect)
def visit_insert_from_select(element, compiler, **kw):
    return "INSERT INTO %s (%s)" % (
        compiler.process(element.table, asfrom=True, **kw),
        compiler.process(element.select, **kw)
    )
insert = InsertFromSelect(t1, select(t1).where(t1.c.x>5))
print(insert)

产生:

"INSERT INTO mytable (SELECT mytable.x, mytable.y, mytable.z
                      FROM mytable WHERE mytable.x > :x_1)"

注意

上述的 InsertFromSelect 构造只是一个例子,实际功能已经可以使用 Insert.from_select() 方法实现。

在 SQL 和 DDL 编译器之间进行交叉编译

SQL 和 DDL 构造使用不同的基础编译器 - SQLCompilerDDLCompiler 进行编译。常见的需要是从 DDL 表达式中访问 SQL 表达式的编译规则。因此,DDLCompiler 包含一个访问器 sql_compiler,如下所示,我们生成一个嵌入了 SQL 表达式的 CHECK 约束:

@compiles(MyConstraint)
def compile_my_constraint(constraint, ddlcompiler, **kw):
    kw['literal_binds'] = True
    return "CONSTRAINT %s CHECK (%s)" % (
        constraint.name,
        ddlcompiler.sql_compiler.process(
            constraint.expression, **kw)
    )

在上面的例子中,我们在由 SQLCompiler.process() 调用的处理步骤中添加了一个额外的标志,即 literal_binds 标志。这表示任何引用 BindParameter 对象或其他“文字”对象(如引用字符串或整数的对象)的 SQL 表达式应该就地渲染,而不是作为一个绑定参数引用;在发出 DDL 时,通常不支持绑定参数。

在 SQL 和 DDL 编译器之间进行交叉编译

SQL 和 DDL 构造使用不同的基础编译器 - SQLCompilerDDLCompiler 进行编译。常见的需要是从 DDL 表达式中访问 SQL 表达式的编译规则。因此,DDLCompiler 包含一个访问器 sql_compiler,如下所示,我们生成一个嵌入了 SQL 表达式的 CHECK 约束:

@compiles(MyConstraint)
def compile_my_constraint(constraint, ddlcompiler, **kw):
    kw['literal_binds'] = True
    return "CONSTRAINT %s CHECK (%s)" % (
        constraint.name,
        ddlcompiler.sql_compiler.process(
            constraint.expression, **kw)
    )

在上面的例子中,我们在由 SQLCompiler.process() 调用的处理步骤中添加了一个额外的标志,即 literal_binds 标志。这表示任何引用 BindParameter 对象或其他“文字”对象(如引用字符串或整数的对象)的 SQL 表达式应该就地渲染,而不是作为一个绑定参数引用;在发出 DDL 时,通常不支持绑定参数。

更改现有构造的默认编译

编译器扩展同样适用于现有构造。当重写内置 SQL 构造的编译时,@compiles 装饰器会在适当的类上调用(确保使用类,即 InsertSelect,而不是创建函数,如 insert()select())。

在新的编译函数中,要获取“原始”编译例程,使用适当的 visit_XXX 方法 - 这是因为编译器.process() 将调用重写例程并导致无限循环。例如,要向所有插入语句添加“前缀”:

from sqlalchemy.sql.expression import Insert
@compiles(Insert)
def prefix_inserts(insert, compiler, **kw):
    return compiler.visit_insert(insert.prefix_with("some prefix"), **kw)

上述编译器在编译时将所有 INSERT 语句前缀为“some prefix”。

更改类型的编译

compiler 也适用于类型,比如下面我们为 String/VARCHAR 实现 MS-SQL 特定的 ‘max’ 关键字:

@compiles(String, 'mssql')
@compiles(VARCHAR, 'mssql')
def compile_varchar(element, compiler, **kw):
    if element.length == 'max':
        return "VARCHAR('max')"
    else:
        return compiler.visit_VARCHAR(element, **kw)
foo = Table('foo', metadata,
    Column('data', VARCHAR('max'))
)

子类指南

使用编译器扩展的一个重要部分是子类化 SQLAlchemy 表达式构造。为了使这更容易,表达式和模式包含一组用于常见任务的“基类”。概要如下:

  • ClauseElement - 这是根表达式类。任何 SQL 表达式都可以从这个基类派生,对于像专门的 INSERT 语句这样的较长构造来说,这可能是最好的选择。
  • ColumnElement - 所有“列样”元素的根。您在 SELECT 语句的“columns”子句中(以及 order by 和 group by)中放置的任何内容都可以从这里派生 - 该对象将自动具有 Python 的“比较”行为。
    ColumnElement 类希望有一个 type 成员,该成员是表达式的返回类型。这可以在构造函数的实例级别或在类级别(如果通常是常量)中建立:
class timestamp(ColumnElement):
    type = TIMESTAMP()
    inherit_cache = True
  • FunctionElement - 这是 ColumnElement 和“from clause”类似对象的混合体,表示 SQL 函数或存储过程类型的调用。由于大多数数据库支持类似“SELECT FROM ”的语句,FunctionElement 添加了在 select() 构造的 FROM 子句中使用的能力:
from sqlalchemy.sql.expression import FunctionElement
class coalesce(FunctionElement):
    name = 'coalesce'
    inherit_cache = True
@compiles(coalesce)
def compile(element, compiler, **kw):
    return "coalesce(%s)" % compiler.process(element.clauses, **kw)
@compiles(coalesce, 'oracle')
def compile(element, compiler, **kw):
    if len(element.clauses) > 2:
        raise TypeError("coalesce only supports two arguments on Oracle")
    return "nvl(%s)" % compiler.process(element.clauses, **kw)
  • ExecutableDDLElement - 所有 DDL 表达式的根,比如 CREATE TABLE,ALTER TABLE 等。 ExecutableDDLElement 的子类的编译由 DDLCompiler 发出,而不是 SQLCompilerExecutableDDLElement 还可以与诸如 DDLEvents.before_create()DDLEvents.after_create() 等事件钩子一起用作事件钩子,允许在 CREATE TABLE 和 DROP TABLE 序列期间自动调用构造。
    另请参阅
    自定义 DDL - 包含将 DDL 对象(它们本身是 ExecutableDDLElement 实例)与 DDLEvents 事件钩子相关联的示例。
  • Executable - 这是一个混合类,应该与表示“独立”SQL 语句的任何表达式类一起使用,可以直接传递给execute()方法。 它已经隐式地存在于 DDLElementFunctionElement 中。

上述大多数构造也会响应 SQL 语句缓存。 子类化的构造将希望为对象定义缓存行为,这通常意味着将标志 inherit_cache 设置为 FalseTrue 的值。 有关背景信息,请参见下一节 为自定义构造启用缓存支持。


SqlAlchemy 2.0 中文文档(三十七)(3)https://developer.aliyun.com/article/1562701

相关实践学习
使用PolarDB和ECS搭建门户网站
本场景主要介绍基于PolarDB和ECS实现搭建门户网站。
阿里云数据库产品家族及特性
阿里云智能数据库产品团队一直致力于不断健全产品体系,提升产品性能,打磨产品功能,从而帮助客户实现更加极致的弹性能力、具备更强的扩展能力、并利用云设施进一步降低企业成本。以云原生+分布式为核心技术抓手,打造以自研的在线事务型(OLTP)数据库Polar DB和在线分析型(OLAP)数据库Analytic DB为代表的新一代企业级云原生数据库产品体系, 结合NoSQL数据库、数据库生态工具、云原生智能化数据库管控平台,为阿里巴巴经济体以及各个行业的企业客户和开发者提供从公共云到混合云再到私有云的完整解决方案,提供基于云基础设施进行数据从处理、到存储、再到计算与分析的一体化解决方案。本节课带你了解阿里云数据库产品家族及特性。
相关文章
|
4月前
|
SQL 关系型数据库 数据库
SqlAlchemy 2.0 中文文档(三十八)(4)
SqlAlchemy 2.0 中文文档(三十八)
36 0
|
4月前
|
SQL 缓存 数据库
SqlAlchemy 2.0 中文文档(三十八)(5)
SqlAlchemy 2.0 中文文档(三十八)
35 0
|
4月前
|
SQL 缓存 关系型数据库
SqlAlchemy 2.0 中文文档(四十一)(6)
SqlAlchemy 2.0 中文文档(四十一)
57 7
|
4月前
|
SQL 缓存 关系型数据库
SqlAlchemy 2.0 中文文档(四十一)(7)
SqlAlchemy 2.0 中文文档(四十一)(8)
56 5
|
4月前
|
SQL JSON 关系型数据库
SqlAlchemy 2.0 中文文档(四十一)(5)
SqlAlchemy 2.0 中文文档(四十一)
48 6
|
4月前
|
SQL 缓存 关系型数据库
SqlAlchemy 2.0 中文文档(四十一)(4)
SqlAlchemy 2.0 中文文档(四十一)
38 4
|
4月前
|
SQL 缓存 关系型数据库
SqlAlchemy 2.0 中文文档(四十一)(3)
SqlAlchemy 2.0 中文文档(四十一)
46 4
|
4月前
|
SQL 存储 缓存
SqlAlchemy 2.0 中文文档(三十七)(4)
SqlAlchemy 2.0 中文文档(三十七)
38 1
|
4月前
|
SQL 存储 缓存
SqlAlchemy 2.0 中文文档(三十七)(3)
SqlAlchemy 2.0 中文文档(三十七)
31 1
|
4月前
|
SQL 缓存 API
SqlAlchemy 2.0 中文文档(三十七)(5)
SqlAlchemy 2.0 中文文档(三十七)
21 1