繁体   English   中英

SQLAlchemy引用案例表达式中的子查询

[英]SQLAlchemy reference a subquery from a case expression

我有一个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)

Order1的状态应为“已接收”,Order2的状态应为“ partially_received”。

但是,当我查询“ received”时,我什么也没有,而当查询“ partially_received”时,我得到2个结果,而不是一个。

看来它没有按Order.id过滤LineItems,因此它使用all来计算状态(因为total_qty为4且接收到的总数为2,这将给出“ partially_received”)

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,“'where子句'中的未知列'orders.id'”“)[SQL:u'SELECT count(*)AS count_1 \\ nFROM(SELECT * \\ nFROM订单\\ nWHEREorders.account_id =%s AND(当(a_3.quantity_received =%s时选择情况)THEN%s当(a_3.dif =%s)THEN%s当(a_3.dif>%s)THEN%s当(a_3.dif <% 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'] [参数:(1L,0,'未接收',0,'已接收',0,'部分接收',0,'超接收', '已取消',u'over_received')]

似乎您正在尝试将表达式与最外面的查询相关联,但是事实证明,当前的嵌套子查询方法在MySQL中不可行,因为与其他某些查询相比,它根本不允许FROM子句中的相关子查询根本不允许与以前的FROM列表项关联的数据库,除非使用LATERAL。

另一方面,嵌套的子查询是多余的,因为您可以在SELECT列表的CASE表达式中使用聚合,但是在当前的子查询中,您会混合使用非聚合和聚合表达式:

SELECT li.quantity_received, sum(li.quantity - li.received) as 'dif'

这很可能不是您想要的。 其他一些数据库甚至不允许执行这样的查询,但是如果禁用了li.quantity_received ,MySQL会默默地从组中未指定的行中选择li.quantity_received的值。 默认情况下,在5.7.5及更高版本中已启用该功能,您应该考虑启用它。 查看您的混合资产的另一半,您似乎也有可能要收取已接收数量的总和。

以下是状态表达式的一个版本,可以满足您在问题中介绍的两个测试用例:

 @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处理。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM