我有这个“复杂”(将其转换为sqla核心的复杂对象)postgres查询,它通过不同的因子和表来计算分数。我将分数作为汇总值,但是我更喜欢将其设为无状态,因此我试图将其定义为混合属性和表达式。 hybrid属性很简单,使用python和voila进行了一些简单的数学运算和条件运算。
是否有机会不使用sqlalchemy核心表达式而仅使用原始SQL来实现这一目标?如果是,那么对于原始操作(而不是动态操作)来说,也将对将来的操作非常有用,SQL查询比使用sqla核心函数更容易编写。如果没有,那么如果您能指出正确的方向,仅声明'C:\Windows\system32\WindowsPowerShell\v1.0\Modules\'
,而无需使用sqla核心语言的内部子查询,将不胜感激。
我尝试使用波纹管表达式,但会引发错误:
with clause
也直接从引擎执行查询
@score.expression
def score(cls):
raw = text(calc_score_raw_q) # The sql query bellow
raw.bindparams(product_id=cls.id)
return db.session.query(ProductModel).from_statement(raw).scalar()
,但最终出现相同的错误。
错误
db.engine.execute(raw)
看起来,混合表达式的sqlalchemy.exc.StatementError: (sqlalchemy.exc.InvalidRequestError) A value is required for bind parameter 'product_id'
是一种插补属性,因此它不提供任何值。
SQL查询:
cls.id
答案 0 :(得分:0)
我通过通读SQLA的核心文档设法解决了这个问题。该代码很麻烦,但是可以工作。我复制了与原始帖子完全相同的原始SQL查询
@score.expression
def score(cls):
# Case clauses
desc = case([
(ProductModel.description != None, 0.5),
], else_=0).label('desc')
ship_cost = case([
(ProductModel.shipping_cost != None, 2),
], else_=0).label('ship_c')
dur = case([
(ProductModel.duration != None, 0.5)
], else_=0).label('dur')
# ibp = item body points, SELECT ( CASE ... as a)
ibp_q = select([desc, ship_cost, dur]) \
.where(ProductModel.id == cls.id).alias()
ibp_q = select([func.sum(
ibp_q.c.desc + ibp_q.c.dur + ibp_q.c.ship_c + 1
).label('gdp')]).cte('attr_points')
# total degrees, SELECT COUNT(*) as td ...
td_q = select([func.count(product_degrees.c.deal_id).label('total')]).where(product_degrees.c.deal_id == cls.id).cte('total_degrees') # cte creates the aliases inside "with" clause
# total score logic, SELECT SUM( CASE WHEN td <= 50 ....
total_q = func.sum(case(
[
(td_q.c.total <= 50, td_q.c.total + ibp_q.c.gdp),
(td_q.c.total <= 200, 50 + ibp_q.c.gdp + (td_q.c.total - 50) * 1.5),
], else_=275 + ibp_q.c.gdp + (td_q.c.total - 200) * 2
)).label('total_points')
# Construct the whole query. select_from assigns the aliases and renders a "with" clause
q = select([total_q]).select_from(td_q).select_from(ibp_q).as_scalar()
return q