Rails 3 添加数据库名称到 table 使包含默认为预加载
Rails 3 adding database name to table makes includes default to eager load
来源:
- http://emphaticsolutions.com/2009/11/23/has_many_through_across_databases.html
- http://blog.arkency.com/2013/12/rails4-preloading/
我的项目正朝着拥有多个数据库(目前在同一台服务器上)的方向发展,我希望能够在这些数据库之间加入。为此,我需要将数据库名称添加到 table 前缀,如下所示:
class FirstBase < ActiveRecord::Base
def self.table_name_prefix
"DBNAME.t_"
end
establish_connection :firstdb
end
class User < FirstBase
has_many :user_roles
end
class UserRole < FirstBase
belongs_to :user
end
添加 table 名称前缀似乎会影响 include 在同一查询中的默认行为,即使在同一数据库中也是如此。考虑 User.includes(:user_roles).first
没有table名字前缀:
User Load (67.1ms) SELECT t_users
.* FROM t_users
LIMIT 1
UserRole Load (84.5ms) SELECT t_user_roles
.* FROM t_user_roles
WHERE
t_user_roles
.user_id
IN (1)
带table名字前缀:
SQL (76.8ms) SELECT DISTINCT DBNAME
.t_users
.id FROM DBNAME
.t_users
LEFT OUTER JOIN DBNAME
.t_user_roles
ON DBNAME
.t_user_roles
.user_id
= DBNAME
.t_users
.id
LIMIT 1
SQL (66.4ms) SELECT DBNAME
.t_users
.id
AS t0_r0,DBNAME
.t_users
.email
AS t0_r1, DBNAME
.t_user_roles
.id
AS t1_r0, DBNAME
.t_user_roles
.user_id
AS t1_r1 FROM DBNAME
.t_users
LEFT OUTER JOIN DBNAME
.t_user_roles
ON DBNAME
.t_user_roles
.user_id
= DBNAME
.t_users
.id
WHERE DBNAME
.t_users
.id
IN (1)
换句话说,调用 include 的默认行为已从预加载更改为预加载。
有谁知道为什么默认行为会改变?一定有一些关于添加数据库名称的内容使 Rails 认为我们必须预先加载,但我不明白为什么。我也很惊讶地看到这一点,因为我认为添加数据库名称并不罕见。我可以通过将所有包含更改为预加载来在我们的代码库中强制修复此问题,但我想了解这里发生了什么。有没有办法改变默认行为?
问题是 table_name_prefix
引入了句点。这混淆了试图确定它是否应该预加载或预加载的逻辑。这是一个 Rails 3 错误,已在 Rails 4 中解决。如果您需要 Rails 3 中的特定行为,则需要明确指定 preload
或 eager_load
正如您在问题中指出的那样。
在 ActiveRecord::Relation 中,exec_queries
调用 eager_loading?
来决定是否应该预先加载。这会调用 references_eager_loaded_tables?
,它使用 tables_in_string
尝试在 SQL 查询中查找不属于已连接 table 的 table 名称:
# ActiveRecord::Relation#references_eager_loaded_tables?
(tables_in_string(to_sql) - joined_tables).any?
tables_in_string
方法存在缺陷,因为它并不总是正确解析 SQL。此代码可用于查看它认为 SQL 查询中的 table 名称:
relation = User.includes(:user_roles)
relation.send(:tables_in_string, relation.to_sql)
使用 DBNAME.t_
table 名称前缀,这将给 ["DBNAME", "t_users"]
作为 table 名称,这是错误的。它应该给出 ["DBNAME.t_users"]
.
ActiveRecord query changing when a dot/period is in condition value. This led to changes in ActiveRecord::Relation 中记录了一个类似的问题,在决定是预加载还是预先加载时不再使用 tables_in_string
。
来源:
- http://emphaticsolutions.com/2009/11/23/has_many_through_across_databases.html
- http://blog.arkency.com/2013/12/rails4-preloading/
我的项目正朝着拥有多个数据库(目前在同一台服务器上)的方向发展,我希望能够在这些数据库之间加入。为此,我需要将数据库名称添加到 table 前缀,如下所示:
class FirstBase < ActiveRecord::Base
def self.table_name_prefix
"DBNAME.t_"
end
establish_connection :firstdb
end
class User < FirstBase
has_many :user_roles
end
class UserRole < FirstBase
belongs_to :user
end
添加 table 名称前缀似乎会影响 include 在同一查询中的默认行为,即使在同一数据库中也是如此。考虑 User.includes(:user_roles).first
没有table名字前缀:
User Load (67.1ms) SELECT
t_users
.* FROMt_users
LIMIT 1 UserRole Load (84.5ms) SELECTt_user_roles
.* FROMt_user_roles
WHEREt_user_roles
.user_id
IN (1)
带table名字前缀:
SQL (76.8ms) SELECT DISTINCT
DBNAME
.t_users
.id FROMDBNAME
.t_users
LEFT OUTER JOINDBNAME
.t_user_roles
ONDBNAME
.t_user_roles
.user_id
=DBNAME
.t_users
.id
LIMIT 1SQL (66.4ms) SELECT
DBNAME
.t_users
.id
AS t0_r0,DBNAME
.t_users
.DBNAME
.t_user_roles
.id
AS t1_r0,DBNAME
.t_user_roles
.user_id
AS t1_r1 FROMDBNAME
.t_users
LEFT OUTER JOINDBNAME
.t_user_roles
ONDBNAME
.t_user_roles
.user_id
=DBNAME
.t_users
.id
WHEREDBNAME
.t_users
.id
IN (1)
换句话说,调用 include 的默认行为已从预加载更改为预加载。
有谁知道为什么默认行为会改变?一定有一些关于添加数据库名称的内容使 Rails 认为我们必须预先加载,但我不明白为什么。我也很惊讶地看到这一点,因为我认为添加数据库名称并不罕见。我可以通过将所有包含更改为预加载来在我们的代码库中强制修复此问题,但我想了解这里发生了什么。有没有办法改变默认行为?
问题是 table_name_prefix
引入了句点。这混淆了试图确定它是否应该预加载或预加载的逻辑。这是一个 Rails 3 错误,已在 Rails 4 中解决。如果您需要 Rails 3 中的特定行为,则需要明确指定 preload
或 eager_load
正如您在问题中指出的那样。
在 ActiveRecord::Relation 中,exec_queries
调用 eager_loading?
来决定是否应该预先加载。这会调用 references_eager_loaded_tables?
,它使用 tables_in_string
尝试在 SQL 查询中查找不属于已连接 table 的 table 名称:
# ActiveRecord::Relation#references_eager_loaded_tables?
(tables_in_string(to_sql) - joined_tables).any?
tables_in_string
方法存在缺陷,因为它并不总是正确解析 SQL。此代码可用于查看它认为 SQL 查询中的 table 名称:
relation = User.includes(:user_roles)
relation.send(:tables_in_string, relation.to_sql)
使用 DBNAME.t_
table 名称前缀,这将给 ["DBNAME", "t_users"]
作为 table 名称,这是错误的。它应该给出 ["DBNAME.t_users"]
.
ActiveRecord query changing when a dot/period is in condition value. This led to changes in ActiveRecord::Relation 中记录了一个类似的问题,在决定是预加载还是预先加载时不再使用 tables_in_string
。