将同一列与总和相加,以及基于不同条件的同一列的部分总和

Posted

技术标签:

【中文标题】将同一列与总和相加,以及基于不同条件的同一列的部分总和【英文标题】:Sum same column as total sum, and also partial sums from that same column based on different conditions 【发布时间】:2021-09-21 20:18:58 【问题描述】:

我的架构如下所示:

Purchase 属于 OrderItem,OrderItem 属于一个 Order。购买属于order through order_items的订单。

购买有一个称为金额的列,订单有一个称为网关交易ID的列。当订单有 gateway_transaction_id 时,购买被认为是在线的。而当订单没有gateway_transaction_id时,则认为购买下线。

目前我需要做total_sum of purchase、total_sum of online purchase、total_sum of offline purchase。

这是我目前拥有的:

all_active_purchases.joins(:order).selecting 
          [
            sum(amount).as('total_purchase_amount'),
            count(id).as('total_purchases_count'),
            count(distinct(purchaser_id)).as('total_purchaser_count')
          ]

这是使用 baby_squeel gem 完成的。

我可以像这样为捐赠添加在线和离线范围:

scope :online ->  joins(:order).where.not(orders: gateway_transaction_id: nil)
scope :offline ->  joins(:order).where(orders: gateway_transaction_id: nil)

然后我可以得到另外两个总和:

purchases.online.sum(:amount)
purchases.offline.sum(:amount)

但是感觉没必要最后两个查询。我想计算所有值作为计算 total_purchase_amount 的第一个查询的一部分,以减少数据库查询的数量。是否可以将所有这些计算为一个查询的一部分?我将标记 mysql 标记,并且我可以在应用程序中使用 mysql 语法。

以下是相关架构:

create_table "purchases", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci", force: :cascade do |t|
    t.decimal "amount", precision: 10, scale: 2, null: false
    t.integer "purchaser_id", null: false
end
create_table "order_items", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci", force: :cascade do |t|
    t.integer "order_id"
    t.string "item_type"
    t.integer "item_id"
end
create_table "orders", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci", force: :cascade do |t|
      t.string "gateway_transaction_id"
end

示例数据:

Purchases:
id: 1, amount: 20
id: 2, amount: 30
order_items:
id: 1, item_type: "Purchase", item_id: 1, order_id: 1
id: 2, item_type: "Purchase", item_id: 2, order_id: 2
order:
id: 1, gateway_transaction_id: 'abcdef'
id: 2, gateway_transaction_id: 

id 为 1 的购买将是在线购买,因为它的订单有一个网关交易 id,而 id 为 2 的购买将是离线的,因为它的订单为 nil gateway_transaction_id

【问题讨论】:

能否添加表的架构、示例数据和预期输出? 采购表:金额(十进制) order_item_id(整数,foreign_key); order_items(表) order_id(整数,外键); id orders (table) id (integer) gateway_transaction_id (string) @SebastianPalma 您可以从 db/schema.rb 文件(或 db/structure.sql)中获取架构。仍然没有示例数据和预期输出,我(我们)看到的和你看到的不一样,我们也不知道你知道的一样。为了帮助您,我们必须首先更好地了解您拥有什么以及您想要做什么。 @SebastianPalma 谢谢,我已经添加了架构和示例数据。 purchase 表和其他表有什么关系?我看到您的范围可以访问订单,但没有看到它们之间的关系。 【参考方案1】:

您可以使用SUMCASE 语句来获得“过滤”聚合:

all_active_purchases.joins(:orders)
        .select(
           'SUM(CASE WHEN orders.gateway_transaction_id IS NULL THEN purchases.amount ELSE 0 END) AS offline_amount',
           'SUM(CASE WHEN orders.gateway_transaction_id IS NOT NULL THEN purchases.amount ELSE 0 END) AS online_amount'
        )

【讨论】:

我不太了解 squeel 来翻译这个,所以我把它留给你。你可能想看看***.com/questions/60411301/case-statements-in-arel

以上是关于将同一列与总和相加,以及基于不同条件的同一列的部分总和的主要内容,如果未能解决你的问题,请参考以下文章

同一表的不同列的计数总和

用于计算同一列的值百分比的 SQL [关闭]

如何将数据框中的特定列与同一数据框中的一个特定列相乘?

如何根据另一列的条件查询同一列两次?

SQL 合并总和选择一个结果

如何在同一列或不同列的一个sql语句中两次使用'BETWEEN'条件