我有一个hybrid_property,它根据一对多关系的一些计算返回一个字符串。
hybrid_property表达式的原始sql是: 这是原始的sql:
SELECT
CASE
WHEN s.quantity_received = 0 THEN "unreceived"
WHEN s.dif = 0.0 THEN "received"
WHEN s.dif > 0.0 THEN "partially_received"
WHEN s.dif < 0.0 THEN "over_received"
END as status
FROM (
SELECT li.quantity_received, sum(li.quantity - li.received) as 'dif'
FROM line_items as li
WHERE li.o_id = xxx
) as s
模特
class LineItem(BaseModel):
__table__ = Table('line_items', autoload=True)
order = relationship("Order", backreef="line_itms", primaryjoin="Order.id == foregin(LineItem.o_id)")
class Order(BaseModel):
__table__ = Table('orders', autoload=True)
@hybrid_property
def status(self):
qty_received, qty_ordered = 0, 0
for li in self.line_items:
if li.status != "cancelled":
qty_ordered += li.quantity
qty_received += li.quantity_received
if qty_received == 0:
status = "unreceived"
elif qty_received == qty_ordered:
status = "received"
elif qty_received < qty_ordered:
status = "partially_received"
elif qty_received > qty_ordered:
status = "over_received"
return status
@status.expression
def status(cls):
line_items_calc = select([LineItem.quantity_received,
func.sum(LineItem.quantity - LineItem.quantity_received).label('dif')]) \
.where(and_(LineItem.o_id == Order.id,
or_(LineItem.fulfillment_status != "cancelled",
LineItem.fulfillment_status == None))) \
.alias()
qq = select([
case([
(qs.c.quantity_received == 0, "unreceived"),
(qs.c.dif == 0, "received"),
(qs.c.dif > 0, "partially_received"),
(qs.c.dif < 0, "over_received")]
)]) \
.select_from(line_items_calc) \
.as_scalar()
return qq
我有2个订单,o1和o2包含订单项:
LineItem(o_id=o1.id, quantity=1, quantity_received=1)
LineItem(o_id=o2.id, quantity=1, quantity_received=0)
LineItem(o_id=o2.id, quantity=2, quantity_received=1)
订单1应具有状态&#34;已收到&#34;而Order2应该有&#34; partial_received&#34;。
但当我查询&#34;收到&#34;我什么都没得到,在查询&#34; partial_received&#34;我得到2个结果而不是一个。
看起来它没有通过Order.id过滤LineItems,所以它使用all来计算状态(因为total_qty将是4,而接收的总数将是2,这将给出&#34; partial_received&#34;)
Order.query().filter(Order.status == 'received').all() # returns []
Order.query().filter(Order.status == 'partially_received').all() # returns [Order1, Order2]
如果将.correlate_except(LineItem)
添加到 line_items_calc 查询,我会收到以下错误:
OperationalError:(_ mysql_exceptions.OperationalError)(1054, &#34;未知列&#39; orders.id&#39;在&#39; where子句&#39;&#34;)[SQL:你&#39; SELECT count(*)AS count_1 \ nFROM(SELECT * \ nFROM命令\ nWHERE orders.account_id =%s AND(SELECT CASE WHEN(a_3.quantity_received = %s)THEN%s WHEN(a_3.dif =%s)THEN%s WHEN(a_3.dif&gt;%s)THEN%s WHEN(a_3.dif&lt;%s)THEN%s END as a_2 \ nFROM(SELECT line_items.quantity_received AS quantity_received, sum(line_items.quantity - line_items.quantity_received)AS dif \ nFROM line_items \ nWHERE line_items.o_id = orders.id AND (line_items.fulfillment_status!=%s OR line_items.fulfillment_status IS NULL))AS a_3)=%s)AS a_1&#39;] [参数:(1L,0,&#39;未接收&#39;,0, &#39;收到&#39;,0,&#39;部分接收&#39;,0,&#39; over_received&#39;,&#39;取消&#39;, U&#39; over_received&#39)]
答案 0 :(得分:1)
似乎你试图将表达式与最外层的查询相关联,但事实证明当前嵌套的子查询方法在MySQL中是不可行的,因为它does not allow correlated subqueries in FROM clause根本没有 - 与其他一些相比数据库只是不允许与以前的FROM列表项相关联,除非使用LATERAL。
另一方面,嵌套子查询是多余的,因为您可以在SELECT列表中的CASE表达式中使用聚合,但在当前子查询中混合使用非聚合和聚合表达式:
SELECT li.quantity_received, sum(li.quantity - li.received) as 'dif'
这很可能不是你想要的。其他一些数据库甚至不允许这样的查询执行,但如果ONLY_FULL_GROUP_BY被禁用,MySQL会默默地从组中未指定的行中选择li.quantity_received
的值。默认情况下,它在5.7.5及更高版本中启用,您应该考虑启用它。看看你的混合财产的另一半看起来你可能也想收取收到数量的总和。
以下是状态表达式的一个版本,它满足您在问题中提出的2个测试用例:
@status.expression
def status(cls):
qty_received = func.coalesce(func.sum(LineItem.quantity_received), 0)
qty_ordered = func.coalesce(func.sum(LineItem.quantity), 0)
return select([case([
(qty_received == 0, "unreceived"),
(qty_received == qty_ordered, "received"),
(qty_received < qty_ordered, "partially_received"),
(qty_received > qty_ordered, "over_received")])]).\
where(and_(func.coalesce(LineItem.fulfillment_status, "") != "cancelled",
LineItem.o_id == cls.id)).\
correlate_except(LineItem).\
as_scalar()
我相信它比你原来更接近Python方法。请注意使用COALESCE进行NULL处理。