数据库结构和计算行

时间:2014-08-25 22:17:03

标签: sql ruby-on-rails database postgresql ruby-on-rails-4

一些背景

我正在建造一些购物车。某些产品(并非全部)可以兑换,如redeemable表中的products字段所示。

兑换产品时,redeemed_qtylocation_id会在line_item_states表中设置为一行。这允许两件事:

  1. 虽然产品最初有位置,但一旦兑换,我们可以为兑换的数量设置新的位置
  2. 您可以多次兑换已购买的商品(最多为总购买数量)
  3. 问题

    我尝试创建一个报告,列出所有line_items,但line_item_states增强/扩展。

    例如,根据下面的数据,它应该列出LineItem 3次:

    Product               | Redeemed? | Qty | Location
    Full Yard Landscaping | No        | 4   | 2
    Full Yard Landscaping | Yes       | 8   | 3
    Full Yard Landscaping | Yes       | 2   | 1
    

    它已列出3次,因为line_item_states的{​​{1}}按位置分组,其line_item求和。

    我能够在没有第一行的情况下实现上表BUT。这是具有计算的剩余未兑换数量的行。所以基本上是某种虚拟行?

    这是我当前查询生成上表(没有第一行):

    redeemed_qty

    问题

    如何展开查询以生成示例表中的第一行?
    它甚至可能吗?我看错了吗?


    信息和样本数据

    数据

    的LineItem:

    LineItem.select('"line_items".*,
            COALESCE("locs"."location_id", "products"."location_id") "location_id",   
            COALESCE("locs"."rqty", "line_items"."qty") "lqty",
            "locs"."rqty" "qty_redeemed"')
    .joins('LEFT OUTER JOIN
                (SELECT "lie"."line_item_id",
                        "lie"."location_id",
                        SUM("lie"."redeemed_qty") "rqty"
                 FROM "line_item_states" "lie"
                 GROUP BY "lie"."line_item_id", "lie"."location_id") "locs"
              ON ("line_items"."id" = "locs"."line_item_id")
            LEFT OUTER JOIN "products"
              ON "products"."id" = "line_items"."product_id"')
    

    产品:

    #<LineItem id: 24, order_id: 19, product_id: 3, created_at: "2014-08-20 01:27:53", updated_at: "2014-08-21 05:51:51", qty: 14, discount: #<BigDecimal:7ff0ba69e238,'0.0',9(18)>, discount_type: nil, price: #<BigDecimal:7ff0ba69e0f8,'0.1069E3',18(18)>, total: #<BigDecimal:7ff0ba69e030,'0.14966E4',18(18)>>
    

    LineItemState:

    #<Product id: 3, title: "Full Yard Landscaping", full_price: #<BigDecimal:7ff0ba4d6400,'0.299E3',9(18)>, created_at: "2014-08-18 02:23:25", updated_at: "2014-08-21 05:50:56", price_overridable: true, deposit_price: #<BigDecimal:7ff0ba4d5910,'0.11E3',9(18)>, gvable: true, redeemable: true, location_id: 2>
    

    产品

    模式

    [#<LineItemState id: 51, redeemed_qty: 1, location_id: 1, user_id: 1, created_at: "2014-08-21 05:26:33", updated_at: "2014-08-21 05:26:33", line_item_id: 24, notes: nil>,
     #<LineItemState id: 52, redeemed_qty: 1, location_id: 3, user_id: 1, created_at: "2014-08-21 05:26:56", updated_at: "2014-08-21 05:26:56", line_item_id: 24, notes: nil>,
     #<LineItemState id: 53, redeemed_qty: 2, location_id: 3, user_id: 1, created_at: "2014-08-21 05:30:30", updated_at: "2014-08-21 05:30:30", line_item_id: 24, notes: nil>,
     #<LineItemState id: 54, redeemed_qty: 1, location_id: 3, user_id: 1, created_at: "2014-08-21 05:31:08", updated_at: "2014-08-21 05:31:08", line_item_id: 24, notes: nil>,
     #<LineItemState id: 55, redeemed_qty: 1, location_id: 3, user_id: 1, created_at: "2014-08-21 05:31:22", updated_at: "2014-08-21 05:31:22", line_item_id: 24, notes: nil>,
     #<LineItemState id: 56, redeemed_qty: 1, location_id: 3, user_id: 1, created_at: "2014-08-21 05:31:59", updated_at: "2014-08-21 05:31:59", line_item_id: 24, notes: nil>,
     #<LineItemState id: 57, redeemed_qty: 1, location_id: 3, user_id: 1, created_at: "2014-08-21 05:58:21", updated_at: "2014-08-21 05:58:21", line_item_id: 24, notes: nil>,
     #<LineItemState id: 58, redeemed_qty: 1, location_id: 3, user_id: 1, created_at: "2014-08-21 06:03:34", updated_at: "2014-08-21 06:03:34", line_item_id: 24, notes: nil>,
     #<LineItemState id: 59, redeemed_qty: 1, location_id: 1, user_id: 1, created_at: "2014-08-21 06:04:20", updated_at: "2014-08-21 06:04:20", line_item_id: 24, notes: "Another cool message dude!">]
    

    模型

    create_table "products", force: true do |t|
      t.string   "title"
      t.decimal  "full_price",        precision: 7, scale: 2, default: 0.0
      t.datetime "created_at"
      t.datetime "updated_at"
      t.boolean  "price_overridable",                         default: false
      t.decimal  "deposit_price",     precision: 7, scale: 2, default: 0.0
      t.boolean  "gvable",                                    default: false
      t.boolean  "redeemable",                                default: false
      t.integer  "location_id"
    end
    

    的LineItem

    模式

    class Product < ActiveRecord::Base
      has_many :line_items
      belongs_to :location
    end
    

    模型

      create_table "line_items", force: true do |t|
        t.integer  "order_id"
        t.integer  "product_id"
        t.datetime "created_at"
        t.datetime "updated_at"
        t.integer  "qty",           limit: 8,                         default: 1
        t.decimal  "discount",                precision: 7, scale: 2, default: 0.0
        t.string   "discount_type"
        t.decimal  "price",                   precision: 7, scale: 2, default: 0.0
        t.decimal  "total",                   precision: 7, scale: 2, default: 0.0
      end
    

    LineItemState

    模式

    class LineItem < ActiveRecord::Base
      belongs_to :product
      has_many :line_item_states
    end
    

    模型

    create_table "line_item_states", force: true do |t|
      t.integer  "redeemed_qty"
      t.integer  "location_id"
      t.integer  "user_id"
      t.datetime "created_at"
      t.datetime "updated_at"
      t.integer  "line_item_id"
      t.text     "notes"
    end
    

1 个答案:

答案 0 :(得分:1)

我建议您使用CTE计算兑换数量。在用于从基本数量中减去的主查询中,并使用UNION ALL附加计算的行。

使用一组基本列,查询可能如下所示:

WITH cte AS (
   SELECT line_item_id, location_id, SUM(redeemed_qty) AS qty
   FROM   line_item_states
   GROUP  BY line_item_id, location_id
   )

SELECT FALSE AS redeemed, li.id AS line_item_id, li.location_id
      ,CASE WHEN r.qty IS NULL THEN li.qty ELSE li.qty - r.qty END AS qty
FROM   line_items li
LEFT   JOIN (
   SELECT line_item_id, sum(qty) AS qty
   FROM   cte
   GROUP  BY 1
   ) r ON r.line_item_id = li.id

UNION  ALL
SELECT TRUE, c.line_item_id, COALESCE(c.location_id, p.location_id), c.qty
FROM   cte c
JOIN   line_items    li ON li.id = l.line_item_id 
LEFT   JOIN products p  ON p.id = li.product_id;