与同一个表相关的两个表的列总和

问题描述 投票:0回答:1

问题是:我有一袋(表A)蔬菜(表B)和水果(表C)。袋子里可以装多种蔬菜和多种水果。每种蔬菜和水果都有价格。

我需要查询返回包中物品的总数量(整数)和价格总和(小数)。袋子可以有 0 个蔬菜,但有 3 个水果,反之亦然,它可以有物品,但是免费的,也可以是空的,但有价格(袋子本身的价格)。所以我们只过滤掉最后有 0 件商品和 0 总价格的袋子。

我能做的最好的就是四个像这样的子查询:

query = select(Bag.id, Bag.purchase_datetime)

vegetable_price = select(func.sum(Vegetable.amount)).where(Vegetable.bag_id == Bag.id).scalar_subquery().label("vegetable_price_total")

vegetable_count = select(func.count(Vegetable.id)).where(Vegetable.bag_id == Bag.id).scalar_subquery().label("vegetable_count_total")

fruit_price = select(func.count(Fruit.id)).where(Fruit.bag_id == Bag.id).scalar_subquery().label("fruit_price_total")

fruit_count = select(func.count(Fruit.id)).where(Fruit.bag_id == Bag.id).scalar_subquery().label("fruit_count_total")

query = add_columns(vegetable_price, vegetable_count, fruit_price, fruit_count)

然后根据路线的参数(仅询问水果、仅询问蔬菜或所有类型),我有一个查询。where(fruit_price + Vegetable_price > 0)。

最后我将这些列添加到路由返回的对象中,如下所示:

result = session.execute(query.distinct())
for b in result:
    yield BagObject(
        id=b.id
        item_count=(getattr(b, "vegetable_count_total", 0) or 0) + (getattr(b, "fruit_count_total", 0) or 0),
        total_price=(getattr(b, "vegetable_price_total", 0) or 0) + (getattr(b, "fruit_price_total", 0) or 0),
    )

即使作为后端开发的初学者,我也知道这很糟糕。我不可能用更少的代码使用更优化的解决方案来做到这一点。我可以从具有更多 SQL 经验的人那里获得帮助吗?

我尝试了其他东西,比如 join、outerjoin。但所有这些都会导致获得重复的行,并且价格/item_count 会在最终结果中相乘。

python sql postgresql sqlalchemy
1个回答
0
投票

您说您尝试过加入,但得到了重复项。这可能是因为您将蔬菜和水果放入了他们的袋子中。在 SQL 中,这会为您提供一个表,其中各行包含一种蔬菜和一种水果,因此对于一袋 3 种蔬菜和 4 种水果,您将生成 3 x 4 = 12 行。

您想要做的是将蔬菜 count 和水果 count 添加到他们的袋子中。

我不清楚你的表中有哪些列。从您的代码中,我假设如下:

水果桌(最好叫fruit_purchase之类的)

列_名称 内容
bag_id 购买包
水果名称 水果名称
金额 本次购买该水果的总金额
价格 本次购买该水果的总价

蔬菜桌(同样,应该称为vegetable_purchase左右)

列_名称 内容
bag_id 购买包
蔬菜名称 蔬菜名称
金额 本次购买该蔬菜的总金额
价格 本次购买的该蔬菜的总价

这是用 SQL 编写的一种方法。 (我不了解 SQLAlchemy,所以我无法帮助您。)

with
  v as
  (
    select
      bag_id,
      sum(amount) as total_amount,
      sum(price) as total_price
    from vegetable
    where amount <> 0 or price <> 0
    group by bag_id
  ),
  f as
  (
    select
      bag_id,
      sum(amount) as total_amount,
      sum(price) as total_price
    from fruit
    where amount <> 0 or price <> 0
    group by bag_id
  )
select
  bag_id,
  coalesce(v.total_amount, 0) as vegetable_amount,
  coalesce(f.total_amount, 0) as fruit_amount,
  coalesce(v.total_amount, 0) + coalesce(f.total_amount, 0) as total_amount,
  coalesce(v.total_price, 0) as vegetable_price,
  coalesce(f.total_price, 0) as fruit_price,
  coalesce(v.total_price, 0) + coalesce(f.total_price, 0) as total_price
from v full outer join f using (bag_id);
© www.soinside.com 2019 - 2024. All rights reserved.