1

私は、以下のデータ関係を持つ倉庫/海運会社向けのプログラムを開発しています。以下の関係についてのスキニーは、倉庫がさまざまな運送業者(クライアント)から原材料(製品)を受け取り、製造工場に出荷する必要があるまでそれらを保管することです。貨物が倉庫を離れるとき、製造施設は各原材料がどの会社からのものであるかを知る必要があります。次のERDを見てください。 ここに画像の説明を入力してください

編集:テキスト形式の私の関係。

出荷.rb

has_many :product_shipments, :dependent => :destroy
has_many :products, :through => :product_shipments

product_shipment.rb

belongs_to :product
belongs_to :shipment

product.rb

has_many :product_shipments
has_many :shipments, :through => :product_shipments, :dependent => :destroy
belongs_to :client

client.rb

  has_many :products, :dependent => :destroy

要件が要求する方法でフォーマットされた方法でクエリを生成するのに問題があります。出荷レポートには日付があり、各クライアントを反復処理して、その特定の日に製造施設に出荷された製品をリストする必要があります。動的に生成し、次のようにフォーマットする必要があります。

Shipment Date: 2013-01-01

Client: 12 Name: ACME Widget Co

Product Name | Product Code | Qty Shipped
_________________________________________ 
nuts & bolts |      gj-3423 |          25
steel sheet  |      4g-2394 |          10
dc Motor     |      cj-c213 |           4


Client: 14 Name: Blah Blah, Inc

Product Name | Product Code | Qty Shipped
_________________________________________ 
spacers      |      gj-3423 |          15
epoxy        |      4g-2394 |          10
dc Motor     |      24-gj19 |           6

Client: 15 Name: Sample Co, Inc

Product Name | Product Code | Qty Shipped
_________________________________________ 
hot roll 48  |      cg-3423 |          15
welding wir  |      4g-2394 |          10
dc Motor     |      c2-1241 |           6
.
.
.
.

問題は、ActiveRecordを使用してクエリを生成することです。以下の例の特定の日付の出荷と製品を簡単に取得できます。原材料の元となった元のクライアントを取得し、そのクライアントの製造施設への出荷を動的に繰り返すことは簡単ではありません。

更新:上記のようにクライアントと出荷をグループ化できるようになりました。ここで、指定された日付に出荷がないクライアントを除外する必要があります。以下は、いくらか正しいものの、それでも恐ろしいO(n)クエリを提供します。これが私が持っているものです。

@clients = Client.includes(:products => :shipments)
@clients.each do |c|
  puts c.name
  c.products.each do |p|
    p.shipments.where("ship_date like '#{@ship_date.strftime("%Y-%m-%d")}%'").each do |s|
      s.product_shipments.joins(:product).each do |ps|
        puts s.bill_of_lading + " " + ps.product_id.to_s + " " + ps.product.product_name.to_s + " " + ps.product.product_code + " " +ps.qty_shipped.to_s + " " + s.ship_date.to_s
        end
    end
  end
end

私の問題は、クライアントから始めて、2012年6月30日に出荷された製品を一覧表示するようにクエリを整理する方法です。クエリはその観点からワッコを取得します。関係がそれほど離れていない場合に、アクティブレコードを使用してクエリを生成する方法がわかりません。

4

2 に答える 2

1

更新:レポートで期待する結果を見て、ProductShipmentからの値(quantity属性など)を引き出す必要があるため、product_shipmentsは、読み込みを熱望しているネストされた関連付けに含める必要があります。そうしないと、ProductShipmentsがインスタンス化されません。結合テーブルとしてのみ機能します。

したがって、Client.includes(:products => shipments)...あなたの代わりに:

@clients = Client.includes(:products => {:product_shipments => :shipment}).
                  where("shipments.ship='#{ship_date}'")

今はあなたのドメインモデルを完全には理解していませんが、ネストされた関連付けがたくさんある場合は、1対1の関係で最も多くの情報を保持しているものを見つけるのが好きです。なぜなら、それらは中心的な部分と見なすことができるからです。この場合、製品と出荷は両方とも「マスターモデル」product_shipmentの拡張として理解できます。

したがって、次のように書くことができます(デメテルの法則に敬意を表して):

class ProductShipment < AR
  def report_details
    s = shipment; p = product
    "#{s.bill_of_lading} #{p.id} #{p.name} #{p.code} #{quantity} #{s.shipped_on}"
  end
end

ここにトリッキーな部分があります:それが書かれているように、:products => {:product_shipments => :shipment}Railsは理解します

product_shipments.shipmentだがしかしproduct_shipment.products

後者は実際にdb呼び出しをトリガーします...(これは避けようとしています)。ありがたいことに、Railsにはポケットに別のトリックがあります:

class ProductShipment < ActiveRecord::Base
  belongs_to :product, inverse_of: :product_shipments
end

class Product < ActiveRecord::Base
  has_many :product_shipments, inverse_of: :product
end

アソシエーションのミラーリングを保証したので、製品を介してproduct_shipmentsをフェッチし、DBでO(n)呼び出しなしでレポートを取得できるようになりました。

@client.map {|c| c.products.map {|p| p.product_shipments} }.flatten.each do |ps|
  puts ps.details
end

更新終了


関連するモデルを熱心にロードする必要があります。そうしないと、悪名高いO(n)クエリが発生します。

Shipment.where(:date => someday).includes({:product => :client}).each do |shipmt|
  puts shipmt.date

  shipmt.product.group_by(&:client).each do |client, products|
    puts client

    products.each do |product|
      puts product.details
    end
  end
end

has_many :throughBTWレールは、ここにまたは関連付けがあることを前提として、出荷から製品に直接結合するhas_and_belongs_to_manyため、結合テーブル(別名product_shipment)を使用する必要はありません。

于 2013-01-11T16:37:07.223 に答える
0

これが私がそれをした方法です。たぶんそれを行うための最良の方法ではありませんが、それは近いです。関係を簡単にロードして正しい方向に送ってくれた@charlysistoに感謝します。ルックアップテーブルの結合クエリでO(n)を取得しますが、さらに悪化する可能性があります。私が答えを改善できるように、どんな改良でもコメントしてください。

@clients = Client.order("clients.id ASC").includes(:products => :shipments).where("shipments.ship_date like '#{ship_date}'")
@clients.each do |c|
  puts c.name
  c.products.each do |p|
    p.shipments.each do |s|
      s.product_shipments.joins(:product).each do |ps|
        puts shipment and products stuff
      end
    end
end
于 2013-01-13T02:04:25.473 に答える