我有一个名为UnitOfWork
的表,它有3列cases_identified
,cases_completed
和cases_double_check
,所有这些都是整数的Postgresql数组。
是否可以编写一个查询(或混合属性)来查找已识别但未包含在已完成或双重检查列中的案例?
这是我提出的,但SQL表达式不起作用:
@hybrid_property
def todo(self):
return [x for x in self.cases_identified
if x not in self.cases_completed and
x not in self.cases_double_check]
@todo.expression
def todo(cls):
return [x for x in cls.cases_identified
if x not in cls.cases_completed and
x not in cls.cases_double_check]
我在测试查询中遇到的错误是:
test = Session.query(UnitOfWork.todo).first()
NotImplementedError: Operator 'contains' is not supported on this expression
答案 0 :(得分:2)
对于这个答案,我假设cls.cases_identified
,cls.cases_completed
和cls.cases_double_check
在Python方面属于postgresql.ARRAY(Integer)
类型。
你的@todo.expression
应该只返回:一个SQL表达式。目前它正在尝试返回一个python列表。引发异常是因为postgresql.ARRAY
不支持in
运算符,但它有一个方法contains
,它映射到Postgresql中的@>
运算符并测试“if元素是参数数组表达式元素的超集“。另一方面,这不是你想要的。你很幸运,那里有if x not in ...
,简单
[x for x in cls.cases_identified]
似乎导致无限循环而不是异常。
广泛地介绍了Postgresql中数组之间的区别here,但是这里是使用SQLAlchemy的方法,首先使用an array constructor:
from sqlalchemy import func
...
class UnitOfWork(...):
@todo.expression
def todo(cls):
# Force correlation of `cls` from outer FROM objects. Use
# the `select()` shorthand method of `FunctionElement`.
identified = func.unnest(cls.cases_identified).select().correlate(cls)
completed = func.unnest(cls.cases_completed).select().correlate(cls)
double_check = func.unnest(cls.cases_double_check).select().correlate(cls)
# Create the subquery statement
stmt = identified.except_(completed.union(double_check))
# Uses the `func` generic for ARRAY constructor from
# subquery syntax. The subquery statement has to be
# converted to a "scalar subquery" in the eyes of SQLA
# (really just a subquery returning 1 col) in order
# to remove it from the FROM clause hierarchy.
return func.array(stmt.as_scalar())
这有一个不提供任何FROM对象的缺点(因为它与封闭查询的所有内容相关),所以你必须像这样发出原始查询:
test = Session.query(UnitOfWork.todo).select_from(UnitOfWork).first()
你也可以使用Postgresql intarray module为无空数组的整数提供特殊的函数和运算符:
class UnitOfWork(...):
@todo.expression
def todo(cls):
return (cls.cases_identified - cls.cases_completed -
cls.cases_double_check)
请注意,您必须先在Postgresql中安装扩展程序:
CREATE EXTENSION intarray;