mysql - 在 rails 4 中包含、加入和选择

标签 mysql ruby-on-rails ruby activerecord

我正在尝试使用 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)

这就像一个魅力,只需一个查询即可返回所有内容。问题是它也从 countrycountry_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/

相关文章:

ruby-on-rails - (Rails + Redmine) 根据用户角色在顶部菜单中显示选项卡

ruby-on-rails - 获取一个月内所有活跃项目

ruby-on-rails - Rails rescue_from 停止执行流程

ruby - 为 `class << self` 内的 eval 提供回溯的正确方法是什么?

php - Codeigniter 从数据库获取结果

PHP PDO 使用循环插入

ruby-on-rails - Rails 2到Rails 3, Controller 中的方法验证不见了?

ruby - 如何使用 Ruby 在目标站点上抓取、构建 session 和启动页面

sql - 对 mySQL 中两个表的总和进行排序

MySQL Foreach 表中的行