2010-06-29 2 views
6

Я проектирование системы инвентаризации, он имеет пользователей, продуктов, buy_leads, заказов (уполномоченные buy_leads) входов и выходов.Инвентарный SQL-запрос без UNION?

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 

входы и выходы имеют значение поколичества. Для того, чтобы получить инвентарь продуктов и конкретных запасы продукта Я использую UNION в двух необработанных запросах SQL, делая выходы величины отрицательной, а затем группу и суммировать их все вместе:

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) 
... 

Любые идеи, или я час ave изменить схему для более удобного? Благодаря!

ответ

2

Слишком много возможностей для решения этой проблемы, что именно вы хотите от этих отчетов?

Заказы на покупку? продукты? входы/выходы?

(Я отправляю это как ответ, потому что я не могу прокомментировать ваш вопрос, я буду обновлять с ответом, если вы могли бы, пожалуйста, просветите меня)

UPDATE

попробовать этот

#on input 
named_scope :by_product, lambda {|id| {:joins => {:orders => {:buy_leads => :products}}, :conditions => ['products.id = ?', id]}} 

и вы можете получить исходные данные, соответствующие этому ID продукта по телефону

Input.by_product(25) 

Если это то, что вы искали, я думаю, что теперь вы можете делать выходные данные по продуктам:]

+0

(входы - выходы), по продукту –

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 
0

решения Виктора терпит неудачу, когда существует несколько «выходные» запись, потому что объединение будет дублировать продукты по ОБЕИМ входам и выходы.

Вместо этого вы должны ПРИСОЕДИНИТЬСЯ к использованию производной таблицы вместо фактической таблицы. Без данных это трудно проверить и продемонстрировать, но вы должны попробовать что-то вроде этого:

"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 

Вы видите, что вы присоединяетесь совокупную сумму выводимой таблицы, сгруппированные по входному идентификатору, а не на выходе таблицы. Это устраняет непреднамеренные дублирования строк в ваших соединениях. Конечно, вы можете добавить больше элементов в список «ON» или «Where» в производной таблице (которую я назвал «z») Это должно сделать вам большую часть пути. В качестве альтернативы, разместите диаграмму DB, чтобы лучше понять ваши отношения с таблицами.

+0

оба подхода кажутся правильными, но они не упрощают работу, я хочу использовать Named Области –

+0

, поэтому поместите запрос в хранимую процедуру? Я думаю, я не уверен, что вы ищете? – Matthew

Смежные вопросы