对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上它却不行。 :(
答
这似乎是在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')
...
这里是工作的查询的例子:http://sqlfiddle.com/#!9/7a6d1/1/0。这同样不适用于Rails .. –
猜测它是外部左连接和左连接产生的行为。看看: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
使用或不使用OUTER的查询都会在数据库上生成所需的输出安慰。看起来问题在于AR如何通过散列来改变它。 –