【问题标题】:Same query on Rails and SQL return different resultsRails 和 SQL 上的相同查询返回不同的结果
【发布时间】:2015-05-21 18:38:11
【问题描述】:

我需要帮助来将此 SQL 转换为 ActiveRecord 查询。

SELECT 
  MAX(total_sales.start_date) AS maximum_start_date,
  customers.external_id, product_categories.external_id AS product_category_id
FROM
  total_sales
INNER JOIN 
  customers
ON
  customers.id = total_sales.customer_id
LEFT JOIN
  product_categories
ON
  product_categories.id = total_sales.product_category_id
WHERE 
  (customers.organization_id = 1)
GROUP BY
  customers.external_id,
  product_categories.external_id

我试过了

TotalSales.joins(:customer).includes(:product_category).
  where("customers.organization_id = ?", 1).
  group("customers.external_id, product_categories.external_id").
  maximum(:start_date)

它几乎可以生成我想要的查询。这是它生成的:

SELECT
  MAX("total_sales"."start_date") AS maximum_start_date, 
  customers.external_id, product_categories.external_id AS customers_external_id_product_categories_external_id
FROM
  "total_sales"
INNER JOIN "customers" ON
  "customers"."id" = "total_sales"."customer_id"
LEFT OUTER JOIN "product_categories" ON
  "product_categories"."id" = "total_sales"."product_category_id" 
WHERE
  (customers.organization_id = 1)
GROUP BY
  customers.external_id, product_categories.external_id

但这会在 Rails 上返回:

=> {"DIA"=>Wed, 01 Jan 2014, "MES"=>Wed, 01 Jan 2014, nil=>Wed, 01 Jan 2014}

如果我在数据库控制台上运行该查询,它会返回

"2014-01-01";"CLIENTE.1";"DIA"
"2014-01-01";"CLIENTE.1";"MES"
"2014-01-01";"CLIENTE.1";""
"2014-01-01";"CLIENTE.10";"DIA"
"2014-01-01";"CLIENTE.10";"MES"
"2014-01-01";"CLIENTE.10";""
"2014-01-01";"CLIENTE.100";"DIA"
"2014-01-01";"CLIENTE.100";"MES"
...

这就是我想要的。在 DB 控制台上它可以工作,但在 Rails 上却不行。 :(

【问题讨论】:

  • 这是一个工作查询的示例:sqlfiddle.com/#!9/7a6d1/1/0。这在 Rails 上不起作用..
  • 猜测是 OUTER LEFT JOIN 与 LEFT JOIN 产生了这种行为。看:stackoverflow.com/questions/8025429/… 还看:codeproject.com/Articles/33052/… 一种方法可能是删除 where 并在获取记录后进行过滤,具体取决于记录集大小
  • 带有或不带有 OUTER 的查询都会在 DB 控制台上产生所需的输出。似乎问题在于 AR 如何在哈希上对其进行转换。
  • 我相信这与两个按列分组具有相同名称的事实有关..

标签: ruby-on-rails ruby postgresql ruby-on-rails-4 activerecord


【解决方案1】:

这似乎是 Rails 中的一个错误。如果您使用group 更改您的线路:

group("customers.external_id, product_categories.external_id").

到:

group("customers.external_id", "product_categories.external_id").

它有效。 注意!你应该使用逗号分隔的两个字符串。

Rails 必须在呈现来自 SQL 的结果时,它认为如果组在一个字符串中,则它只是一个字段,尽管它构建了一个正确的 SQL 查询。

你会得到这个结果:

=> {["CLIENTE.1", "DIA"]=>Wed, 01 Jan 2014, ["CLIENTE.1", "MES"]=>Wed, 01 Jan 2014,
    ["CLIENTE.1", nil]=>Wed, 01 Jan 2014, ["CLIENTE.10", "DIA"]=>Wed, 01 Jan 2014,
    ["CLIENTE.10", "MES"]=>Wed, 01 Jan 2014, ["CLIENTE.10", nil]=>Wed, 01 Jan 2014, ...}

我假设您知道如何使用它。

一个小技巧,使用Arel来避免“硬编码”表名,你可以这样:

cust = Customer.arel_table
prod = ProductCategory.arel_table
TotalSales.joins(:customer).includes(:product_category).
  where(cust[:organization_id].eq(1)).
  group(cust[:external_id], prod[:external_id]).
  maximum(:start_date)

【讨论】:

    【解决方案2】:

    包括 != 左连接

    如果您希望查询以某种方式依赖于othertable,则不应使用includes(othertable),因为includes 具有不同的语义。

    包含你只是说 ActiveRecord 你打算很快从其他表中获取对象,所以它应该针对它进行优化。但它是面向对象的。在您的示例中,您将获得 TotalSales 对象。

    连接是一种关系数据库,其中表的列可以混合。我们通常不会在对象世界中做这样的事情。这就是为什么这经常变得困难。

    如果你需要左连接,你需要告诉 Rails。根据 rails guide,您确实需要使用 SQL 片段。

    TotalSales.joins('LEFT OUTER JOIN product_categories ON product_categories.id = total_sales.product_category_id')
    ...
    

    另请参阅我对这个问题的回答: Rails 4 Relation is not being sorted: 'has_many' association w/ default scope, 'includes' and 'order' chained together

    【讨论】:

      猜你喜欢
      • 2011-06-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-09-29
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多