对Rails和SQL的相同查询返回不同的结果

问题描述:

我需要帮助将此SQL转换为ActiveRecord查询。对Rails和SQL的相同查询返回不同的结果

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 

但这返回on Rails的:

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

如果我运行DB控制台上查询,它返回

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

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

+0

这里是工作的查询的例子:http://sqlfiddle.com/#!9/7a6d1/1/0。这同样不适用于Rails .. –

+0

猜测它是外部左连接和左连接产生的行为。看看:http://stackoverflow.com/questions/8025429/how-can-i-do-a-left-outer-join-using-rails-activerecord另请看:http://www.codeproject.com/文章/ 33052/Visual-Representation of SQL-Joins一种方法可能是在获取记录后删除位置并进行过滤,具体取决于记录集大小 – Mircea

+0

使用或不使用OUTER的查询都会在数据库上生成所需的输出安慰。看起来问题在于AR如何通过散列来改变它。 –

这似乎是在Rails中的一个错误。如果你用group从改变你的线路:

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

到:!

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

它的工作原理NB,你应该使用以逗号分隔的两个字符串

必须在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) 

包括= LEFT JOIN

,当你想你不应该使用includes(othertable)您的查询以某种方式取决于othertable,因为includes它具有不同的语义。

With包括你只是说ActiveRecord,你打算从很快得到的对象,所以它应该优化它。但它是面向对象的。在您的示例中获得TotalSales对象。

连接是一个关系数据库的事情,其中​​表的列可以混合。我们通常不会在物体世界中做这样的事情。那为什么这往往变得困难。

如果你需要一个左连接,你需要告诉rails。根据导轨指南,您确实需要使用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