我正在尝试使用 Rails 4 进行非常简单的查询,但我遇到了意想不到的问题...
我有一个带有翻译表的 Country
模型(通过 globalize gem )。我想获取当前语言环境中的每个国家/地区名称、国家/地区 ID 和存储在我的 Country
模型中的 short_code
。
我的第一个方法是这句话:
countries = Country.select('countries.id, countries.short_code,
country_translations.name').joins(:translations)
这会生成这条 SQL:
SELECT countries.id, countries.short_code, country_translations.name
FROM `countries` INNER JOIN `country_translations`
ON `country_translations`.`country_id` = `countries`.`id`
这似乎是一个正确的查询,如果我在数据库控制台中运行它,它会获得我想要的数据。问题来了,因为我需要序列化countries
。它为每个国家/地区运行查询:
SELECT `country_translations`.* FROM `country_translations` WHERE `country_translations`.`country_id` = 1 [["country_id", 1]]
[...]
SELECT `country_translations`.* FROM `country_translations` WHERE `country_translations`.`country_id` = 238 [["country_id", 238]]
SELECT `country_translations`.* FROM `country_translations` WHERE `country_translations`.`country_id` = 239 [["country_id", 239]]
为了避免 n+1 问题,我尝试在查询中包含
翻译:
countries = Country.select('countries.id, countries.short_code,
country_translations.name').includes(:translations).joins(:translations)
这就像一个魅力,只需一个查询即可返回所有内容。问题是它也从 country
和 country_translations
获取每个属性,忽略了 select 语句!
SELECT countries.id, countries.short_code, country_translations.name,
`countries`.`id` AS t0_r0, `countries`.`population` AS t0_r1,
`countries`.`short_code` AS t0_r2,
`countries`.`lat` AS t0_r3,
`countries`.`lng` AS t0_r4,
`countries`.`range` AS t0_r5,
`countries`.`tilt` AS t0_r6,
`countries`.`heading` AS t0_r7,
`countries`.`country_code_short` AS t0_r8,
`countries`.`date_format` AS t0_r9,
...
`countries`.`created_at` AS t0_r12,
`countries`.`updated_at` AS t0_r13,
`country_translations`.`id` AS t1_r0,
`country_translations`.`country_id` AS t1_r1,
`country_translations`.`locale` AS t1_r2,
`country_translations`.`created_at` AS t1_r3,
`country_translations`.`updated_at` AS t1_r4,
`country_translations`.`name` AS t1_r5,
`country_translations`.`language` AS t1_r6,
`country_translations`.`currency` AS t1_r7,
`country_translations`.`capital` AS t1_r8,
`country_translations`.`slug` AS t1_r9
FROM `countries`
INNER JOIN `country_translations`
ON `country_translations`.`country_id` = `countries`.`id`
这带来了另一个问题,因为有这么多字段,序列化更加复杂......
我错过了什么?我怎样才能得到我需要的那三个字段?
最佳答案
似乎您正在调用 country.name
并期望来自 ActiveRecord 结果的 name
attribute,而 globalize gem 正在调用名称
方法 - 触发 n+1。
使用 ActiveRecord 的 includes方法是解决您提到的 n+1 的一种方法,但具有否定 select
语句和构建比所需更大的 ActiveRecord::Relation 对象的令人讨厌的副作用。此外,您已经在以高效的方式选择所需的字段。
AbM's comment以上指向解决方案;但是 country_translations.name
在您的查询中已经别名为 name
,为了澄清,我会建议类似这样的内容:
countries = Country.select([
'countries.id',
'countries.short_code',
'country_translations.name AS country_translation_name'
])
当遍历 countries
时,您现在可以调用:
country.id # => 840
country.short_code # => "US"
country.country_translation_name # => "United States of America"
关于mysql - 在 rails 4 中包含、加入和选择,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33791392/