【问题标题】:Active Record query with complex join on a derived-table?在派生表上具有复杂连接的 Active Record 查询?
【发布时间】:2013-12-27 22:52:04
【问题描述】:

我正在使用 Ruby、Sinatra 和 MySQL。我有四张桌子。

联系人(客户):

class Contact < ActiveRecord::Base
  attr_accessible :id, :company_id, :name, :address, ...

  has_many :orders, :dependent => :destroy
  has_many :ordered_products
  ...
end

订单:

class Order < ActiveRecord::Base
  attr_accessible :id, :name, :contact_id, ...

  belongs_to :contact  

  has_many :ordered_products, :dependent => :destroy
  has_many :products, :through => :ordered_products
  ...
end

订购产品:

class OrderedProduct < ActiveRecord::Base
  attr_accessible :quantity, :price, :contact_id, :product_id, :order_id ...

  belongs_to :contact
  belongs_to :product
  belongs_to :order
  ...
end

和产品:

class Product < ActiveRecord::Base
  attr_accessible :id, :name, :producer, :region, :size ...

  has_many :ordered_products, :dependent => :destroy
  has_many :orders, :through => :ordered_products
  ...
end

我编写了以下 SQL 查询以获取以联系人为中心的统计信息。

SELECT `contacts`.`name`,
  MIN(orderTotals) AS min,
  AVG(orderTotals) AS avg,
  MAX(orderTotals) AS max,
  SUM(`ordered_products`.`price` * `ordered_products`.`quantity`) AS total,
  CAST(COUNT(DISTINCT `orders`.`id`) AS UNSIGNED) AS ordersNumber,
  CAST(SUM(`ordered_products`.`quantity`) AS UNSIGNED) AS productsNumber
FROM `contacts`
INNER JOIN `orders` ON `orders`.`contact_id` = `contacts`.`id`
INNER JOIN `ordered_products` ON `ordered_products`.`order_id` = `orders`.`id`
INNER JOIN `products` ON `products`.`id` = `ordered_products`.`product_id`
INNER JOIN
  ( 
    SELECT contact_id as identifier,
    SUM(`ordered_products`.`price` * `ordered_products`.`quantity`) as orderTotals
    FROM `ordered_products`
    GROUP BY `ordered_products`.`order_id`
  ) `sumTable`
  ON `sumTable`.`identifier` = `contacts`.`id`
WHERE `contacts`.`company_id` = 74
  AND (`orders`.`updated_at` >= '2013-01-01 00:01:59')
  AND (`orders`.`updated_at` <= '2013-12-31 23:59:59')
  AND (`orders`.`order_state_id` = '100')
GROUP BY `contacts`.`id`
ORDER BY `contacts`.`name` ASC
LIMIT 20
OFFSET 0;

我想把它完全翻译成 Active Record 格式。我能够正确地转换和运行它,但是下面的内部连接让我望而却步,我不得不直接粘贴 SQL:

.joins('INNER JOIN (SELECT contact_id as identifier,
SUM(ordered_products.price * ordered_products.quantity) as orderTotals
FROM `ordered_products` GROUP BY ordered_products.order_id) `sumTable`
ON `sumTable`.`identifier` = `contacts`.`id`').

我只报告翻译后的 SQL 查询的一部分,因为派生表上连接的翻译对我来说最重要。我发现它远没有那么优雅和可读性,例如:

Contact.joins(orders: [{ordered_products: :product}]). ...

任何帮助将不胜感激!

谢谢, 卢卡

【问题讨论】:

  • 嗯。是不是少了一点?
  • 这不是一个完整的 Active Record 查询。请提供整个东西。
  • 所以您想以通常的“AR 方法调用堆”形式将 INNER JOIN 表达到派生表?
  • 在这种情况下,原始查询可能比 ORM 生成的查询更受欢迎
  • 我在这里和@bjhaid 在一起。对于如此复杂的任何事情,我都会绕过 AR 并自己编写 SQL。结果会比一大堆方法调用更清楚,如果有人看不懂SQL,那么他们对“一大堆方法调用”的理解就更少了。不过,AR 对于这类事情来说非常糟糕,你最终不得不跳过愚蠢的圈子来获得正确的参数引用/转义/清理,最终你不得不自己对输出进行去字符串化。查看其他数据库接口以了解此类情况。

标签: mysql ruby activerecord inner-join derived-table


【解决方案1】:

由于没有其余代码,因此无法完全说明您要执行的操作,但如果您使用的是 Postgres,则可能需要查看 pg_search gem,因为它可能会为您节省一些 SQL 工作看起来更新/维护可能会很痛苦。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-04-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多