2010-06-29 14 views
6

Sto progettando un sistema di inventario , ha utenti, prodotti, buy_leads, ordini (buy_leads autorizzati), ingressi e uscite.Query SQL di inventario senza 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 

ingressi e uscite hanno un valorequantità. Per ottenere l'inventario prodotti, e specifico inventario dei prodotti Io uso UNION in due query SQL prime, rendendo uscite quantità negativo, e poi di gruppo e li somma insieme:

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 

Funziona, ma vorrei usare caratteristiche di named_scope a quesiti a catena in ActiveRecord ed essere in grado di fare cose come:

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

Tutte le idee, o devo h per cambiare lo schema per uno più conveniente? Grazie!

risposta

2

Ci sono troppe possibilità per risolvere questo, che cosa vuoi esattamente da questi rapporti?

gli ordini di acquisto? i prodotti? gli input/output?

(sto postando questo come una risposta perché non posso commentare sulla sua domanda, io aggiornare con risposta se si potesse piacere a illuminarmi)

UPDATE

provare questo

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

e si può ottenere gli input che corrispondono quel prodotto id chiamando

Input.by_product(25) 

se questo è quello che stavi cercando penso che ora riesci a rendere le uscite anche per prodotti:]

+0

(ingressi - uscite), per prodotto –

0

non riesco a provare questo senza dati, ma penso che dovrebbe essere qualcosa di simile:

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

soluzione di Victor fallisce quando non ci sono più record di "uscita", perché il join duplicherà i prodotti da entrambi gli ingressi e le uscite.

Invece, dovresti JOIN utilizzare una tabella derivata invece della tabella effettiva. Senza dati questo è difficile da provare e dimostrare, ma si dovrebbe cercare qualcosa di simile:

"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 

Si vede che si stanno unendo la somma aggregata della tabella di output, raggruppati per l'id di ingresso, piuttosto che l'uscita tavolo stesso. Questo elimina le duplicazioni di riga indesiderate nei tuoi join. Ovviamente, puoi aggiungere più elementi alla lista "ON" o alla clausola "Where" della tabella derivata (che ho chiamato "z") Questo dovrebbe farti arrivare quasi tutto lì. In alternativa, aggiungi un diagramma DB per capire meglio le tue relazioni con la tabella.

+0

entrambi gli approcci sembra essere corretto, ma non lo rendono più semplice, voglio usare Named Scopes –

+0

quindi inserire la query in una stored procedure? Immagino di non essere sicuro di cosa stai cercando? – Matthew