版本:1.1.0b2 |发布日期:2016年7月1日

SQLAlchemy 1.1文档

SQL表达式

如何将SQL表达式呈现为字符串,可能绑定参数内联?

绝大多数情况下,SQLAlchemy语句或Query的“字符串化”就像下面这样简单:

print(str(statement))

这适用于ORM Query以及任何select()或其他语句。此外,为了将语句编译成特定的方言或引擎,如果语句本身没有绑定到一个语句,可以将它传递给ClauseElement.compile()

print(statement.compile(someengine))

或者没有Engine

from sqlalchemy.dialects import postgresql
print(statement.compile(dialect=postgresql.dialect()))

当给定一个ORM Query对象时,为了得到ClauseElement.compile()方法,我们只需要首先访问statement

statement = query.statement
print(statement.compile(someengine))

上述形式将在传递给Python DBAPI时呈现SQL语句,其中包括绑定参数不以内联方式呈现。SQLAlchemy通常不绑定绑定的参数,因为这是由Python DBAPI正确处理的,更不用说绕过绑定参数,可能是现代Web应用程序中使用最广泛的安全漏洞。SQLAlchemy在某些情况下(例如发出DDL)的能力有限。为了访问这个功能,可以使用传递给compile_kwargsliteral_binds标志:

from sqlalchemy.sql import table, column, select

t = table('t', column('x'))

s = select([t]).where(t.c.x == 5)

print(s.compile(compile_kwargs={"literal_binds": True}))

上面的方法有一点要注意,它只支持基本类型,例如整数和字符串,而且如果直接使用没有预设值的bindparam(),它将不会被能够把那个串起来。

要支持对不支持类型的内联文字渲染,请为包含TypeDecorator.process_literal_param()方法的目标类型实现一个TypeDecorator

from sqlalchemy import TypeDecorator, Integer


class MyFancyType(TypeDecorator):
    impl = Integer

    def process_literal_param(self, value, dialect):
        return "my_fancy_formatting(%s)" % value

from sqlalchemy import Table, Column, MetaData

tab = Table('mytable', MetaData(), Column('x', MyFancyType()))

print(
    tab.select().where(tab.c.x > 5).compile(
        compile_kwargs={"literal_binds": True})
)

产量如下:

SELECT mytable.x
FROM mytable
WHERE mytable.x > my_fancy_formatting(5)

为什么.col.in_([])产生col != col为什么不1=0

对这个问题稍加介绍。SQL中的IN运算符给定了一个列与要与列进行比较的元素列表,通常不接受一个空列表,也就是说可以这么说:

column IN (1, 2, 3)

说:

column IN ()

SQLAlchemy的Operators.in_()运算符,当给出一个空列表时,产生这个表达式:

column != column

从版本0.6开始,它也会产生一个警告,说明将会呈现效率较低的比较操作。这个表达式是唯一一个既是数据库不可知的,又能产生正确结果的表达式。

例如,“仅仅通过比较1 = 0或1!= 1来评估为假”这种天真的方法,不能正确地处理空值。表达式如下:

NOT column != column

当“列”为空时不会返回一行,但是不考虑列的表达式:

NOT 1=0

将。

更接近标记是以下CASE表达式:

CASE WHEN column IS NOT NULL THEN 1=0 ELSE NULL END

我们不使用这个表达式,因为它的冗长,而且在WHERE子句中它也不被Oracle接受 - 取决于你怎么说,你会得到“ORA-00905:missing keyword”或者“ORA-00920 :无效的关系运算符“。如果不使用子句完全呈现SQL(或者完全不发布SQL,如果语句只是一个简单的搜索),那么它的效率依然不高。

因此,最好的方法是避免给定一个长度为零的参数列表来使用IN。相反,如果不应该返回行,请不要首先发出查询。警告最好使用Python警告过滤器提升为完全错误状态(请参阅http://docs.python.org/library/warnings.html)。