没有UNION的库存SQL查询?

时间:2010-06-29 01:17:36

标签: sql ruby-on-rails sqlite activerecord

我正在设计库存系统,它有用户产品 buy_leads 订单(授权的buy_leads),输入输出

class Product < ActiveRecord::Base
  has_many :buy_leads
end

class BuyLead < ActiveRecord::Base
  belongs_to :product
  has_one :order
end

class Order < ActiveRecord::Base
  belongs_to :buy_lead
  belongs_to :user, :foreign_key => :authorized_by
  has_many :inputs
end

class Input < ActiveRecord::Base
  belongs_to :order
  has_many :outputs
end

class Output < ActiveRecord::Base
  # Associations
  belongs_to :input
  belongs_to :user
end

输入和输出具有数量值。要获取产品库存特定产品库存,我在两个原始SQL查询中使用UNION,输出数量为负,然后分组和把它们总结在一起:

class InventoryController < ApplicationController

  def index
    @inventory = Input.find_by_sql products_inventory_sql
  end

  def show
    @inventory = Input.find_by_sql product_inventory_sql(params[:id])
  end

private

  def inputs_sql
    "SELECT b.*, p.*, i.order_id,
            i.id AS input_id,
            i.quantity AS quantity     
     FROM inputs i
          JOIN orders r ON r.id = i.order_id
          JOIN buy_leads b ON b.id = r.buy_lead_id
          JOIN products p ON p.id = b.product_id"
  end

  def outputs_sql
    "SELECT b.*, p.*, i.order_id,
            i.id AS input_id,
            (o.quantity * -1) AS quantity
     FROM outputs o
          JOIN inputs i ON i.id = o.input_id
          JOIN orders r ON r.id = i.order_id
          JOIN buy_leads b ON b.id = r.buy_lead_id
          JOIN products p ON p.id = b.product_id"
  end

  def products_inventory_sql
    "SELECT *, SUM(quantity) AS remaining_qty
     FROM (#{inputs_sql} UNION #{outputs_sql})
     GROUP BY product_id"
  end

  def product_inventory_sql(id)
    "SELECT *, SUM(quantity) AS remaining_qty
     FROM (#{inputs_sql} UNION #{outputs_sql})
     WHERE product_id = #{id}
     GROUP BY order_id, input_id"
  end

end

它有效,但我想使用 named_scope的功能来链接ActiveRecord中的查询,并能够执行以下操作:

Product.inputs.by_product(id)
Product.inventory.by_product(id)
...

任何想法,还是我必须更改架构以获得更方便的架构?谢谢!

3 个答案:

答案 0 :(得分:2)

答案 1 :(得分:0)

如果没有数据我无法测试,但我认为应该是这样的:

SELECT
        b.*, p.*, i.order_id, i.id AS input_id, i.quantity AS quantity, -o.quantity AS quantity,
        (i.quantity - COALESCE(o.quantity,0)) AS remaining_qty
FROM
        products p
        JOIN buy_leads b ON b.product_id = p.id
        JOIN orders r ON r.buy_lead_id = b.id
        JOIN inputs i ON i.order_id = r.id
        LEFT JOIN outputs o ON o.input_id = i.id

答案 2 :(得分:0)

当有多个“输出”记录时,Victor的解决方案失败,因为连接将通过输入和输出复制产品。

相反,您应该使用派生表而不是实际表来JOIN。 没有数据,这很难测试和演示,但你应该尝试这样的事情:

    "SELECT b.*, p.*, i.order_id,
        i.id AS input_id,
        i.quantity AS quantity,
ISNULL(z.negquantities,0) negquantities,
i.quantity + ISNULL(z.negquantities,0) sumquantities

 FROM inputs i
      JOIN orders r ON r.id = i.order_id
      JOIN buy_leads b ON b.id = r.buy_lead_id
      JOIN products p ON p.id = b.product_id
  JOIN 
    (SELECT SUM(-1 * o.quantity) NegQuantities, o.input_id FROM outputs o GROUP BY o.input_id) z
    ON z.input_id = i.id

您会看到您正在加入输出表的总和,按输入ID分组,而不是输出表本身。这消除了联接中的意外行重复。 当然,您可以在派生表的“ON”列表或“Where”子句中添加更多元素(我称之为“z”) 这应该可以帮助你完成大部分工作。 或者,发布数据库图表,以便我们更好地理解您的表格关系。