【问题标题】:Bitwise operator in SQLAlchemy: Error when using Oracle DatabaseSQLAlchemy 中的位运算符:使用 Oracle 数据库时出错
【发布时间】:2017-03-14 20:57:51
【问题描述】:

我需要使用按位逻辑 AND/OR 编写一些查询,但只有当我使用 oracle 数据库时,才会收到以下错误:

sqlalchemy.exc.DatabaseError: (cx_Oracle.DatabaseError) ORA-01036: 非法变量名称/编号 [SQL: 'SELECT "Cars"."Id", "Cars"."Name", "Cars"."Price" \nFROM "Cars" \nWHERE ("Cars"."Price" & :Price_1) > :param_1'] [参数:{'Price_1': 32768, 'param_1': 0}]

如果我使用 PostgreSql 或 Sqlite,我会收到预期的答案。

create_engine('sqlite:///cars.sqlite3') 好的! create_engine('postgresql+psycopg2://xxx:yyy@localhost:5432/db_sql_alchemy') 好的! create_engine('oracle+cx_oracle://xxx:yyy@localhost:49161/xe') 错误!

select、where 子句、表创建等其他操作在所有 3 个数据库中都按预期工作。

查看错误日志,查询似乎没有正确转换为 oracle 语法。我期待这样的事情:

从“汽车”中选择“汽车”。“ID”,“汽车”。“名称”,“汽车”。“价格” (BitAnd("汽车"."价格", 32768) > 0);

产生错误的操作是:

stm = stm.where(cars.c.Price.op('&')(0x8000) > 0)

我正在使用 Python 2.7.12 和 SQLAlchemy==1.1.2。

【问题讨论】:

  • Oracle 似乎没有 & 运算符。请改用BITAND 函数。
  • op() 泛型按原样呈现。不涉及翻译(iirc)。你得到了你所要求的。
  • 感谢您的回答,但我使用的是 sqlalchemy -SQL Expression Language-。我想,对于任何数据库选择,按位运算的语法都是相同的。我想跳过编写原始 sql,因为我需要使用多个数据库。
  • 每个数据库的语法都不一样。如果您需要透明地支持多个数据库后端,则需要创建自定义构造并相应地自定义编译。
  • 谢谢@univerio。我想我无法理解一些基本概念。当您说“您需要创建自定义构造并相应地自定义编译”时,我需要按照以下步骤操作,因为未定义按位运算符 '&' 并且使用 -meth:.ColumnOperators.op- 时没有编译具体的数据库?在文档中,运算符 + 是根据 DB 选择编译的。在哪些情况下,我们有针对特定引擎的编译?您能否提供更多关于如何“自定义构造和相应地自定义编译”的详细信息、代码示例、模式

标签: python oracle11g sqlalchemy


【解决方案1】:

这是一个创建自定义运算符的示例,该运算符在不同的后端进行不同的编译:

from sqlalchemy.ext.compiler import compiles
from sqlalchemy.sql.elements import ColumnElement, Visitable, Grouping
from sqlalchemy.sql.operators import custom_op, is_precedent

class BitwiseAnd(ColumnElement):
    type = Integer()
    operator = custom_op("&", precedence=6)

    def __init__(self, left, right):
        if not isinstance(left, Visitable):
            left = bindparam("bitand", left, unique=True)
        if not isinstance(right, Visitable):
            right = bindparam("bitand", right, unique=True)
        self.left = left
        self.right = right

    def self_group(self, against=None):
        if is_precedent(self.operator, against):
            return Grouping(self)
        else:
            return self


@compiles(BitwiseAnd)
def _compile_bitwise_and(element, compiler, **kwargs):
    left = element.left.self_group(against=element.operator)
    right = element.right.self_group(against=element.operator)
    return compiler.process(element.operator(left, right))


@compiles(BitwiseAnd, "oracle")
def _compile_bitwise_and_oracle(element, compiler, **kwargs):
    return compiler.process(func.BITAND(element.left, element.right))

q = select([BitwiseAnd(BitwiseAnd(1, 2), BitwiseAnd(3, 4))])
print(q.compile(dialect=mysql.dialect()))
# SELECT (%s & %s) & (%s & %s) AS anon_1
print(q.compile(dialect=oracle.dialect()))
# SELECT BITAND(BITAND(:bitand_1, :bitand_2), BITAND(:bitand_3, :bitand_4)) AS anon_1 FROM DUAL

【讨论】:

  • 非常感谢!这是我使用 SqlAlchemy 的第一个项目,这个过程对我来说不是很清楚。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-09-28
  • 2014-12-29
  • 2013-05-27
  • 1970-01-01
  • 2019-06-09
  • 2015-04-11
  • 2021-10-26
相关资源
最近更新 更多