Skip to main content
 首页 » 编程设计

python之在 SQLAlchemy 中更改 where 子句而不生成子查询

2025年05月04日11kerrycode

我正在尝试构建一个相对复杂的查询,并想直接操作结果的 where 子句,而不是克隆/子查询返回的查询。一个例子看起来像:

    session = sessionmaker(bind=engine)() 
 
    def generate_complex_query(): 
        return select( 
            columns=[location.c.id.label('id')],  
            from_obj=location, 
            whereclause=location.c.id>50 
        ).alias('a') 
 
    query = generate_complex_query() 
    # based on this query, I'd like to add additional where conditions, ideally like: 
    # `query.where(query.c.id<100)` 
    # but without subquerying the original query 
 
    # this is what I found so far, which is quite verbose and it doesn't solve the subquery problem 
    query = select( 
        columns=[query.c.id], 
        from_obj=query, 
        whereclause=query.c.id<100 
    ) 
 
    # Another option I was considering was to map the query to a class: 
    #   class Location(object):pass 
    #   mapper(Location, query) 
    #   session.query(Location).filter(Location.id<100) 
    # which looks more elegant, but also creates a subquery 
 
    result = session.execute(query) 
 
    for r in result: 
        print r 

这是生成的查询:

SELECT a.id  
FROM (SELECT location.id AS id  
FROM location  
WHERE location.id > %(id_1)s) AS a  
WHERE a.id < %(id_2)s 

我想获得:

SELECT location.id AS id  
FROM location  
WHERE id > %(id_1)s and 
id < %(id_2)s 

有什么办法可以实现吗?这样做的原因是我认为查询 (2) 稍微快一点(不多),并且我在适当位置的映射器示例(上面的第二个示例)弄乱了标签(id 变成了 anon_1_id 或 a.id 如果我命名别名)。

请您参考如下方法:

你为什么不这样做:

query = generate_complex_query() 
query = query.where(location.c.id < 100) 

基本上,您可以像这样优化任何查询。此外,我建议阅读 SQL Expression Language Tutorial这非常棒,介绍了您需要的所有技术。构建 select 的方式只是一种方式。通常,我更像这样构建查询:select(column).where(expression).where(next_expression) 等等。 FROM 通常由 SQLAlchemy 从上下文自动推断,即您很少需要指定它。

由于您无权访问 generate_complex_query 的内部结构,请尝试以下操作:

query = query.where(query.c.id < 100) 

我认为这应该适用于您的情况。

另一个想法:

query = query.where(text("id < 100")) 

这使用了 SQLAlchemy 的 text表达。然而,这可能对您有用,而且这重要:如果您想引入变量,请阅读上面链接的 API 的描述,因为只使用格式字符串而不是绑定(bind)参数 会让你接触到 SQL 注入(inject),这对于 SQLAlchemy 来说通常是很简单的事情,但如果使用这样的文字表达式则必须小心。

另请注意,这是可行的,因为您将该列标记为 id。如果您不这样做并且不知道列名,那么这也不起作用。