Arel UpdateManager with Join 创建无效 SQL - 如何改写?
Arel UpdateManager with Join creates invalid SQL - how to rephrase?
显然有一个 issue in Arel core,其中 Arel::UpdateManager
在对连接执行列更新时不会为更新列生成 table 名称。它导致无效 SQL.
我在 Rails 5.2 应用程序中 运行 加入了这个,其中我有一个 SQL 文字更新语句,我试图在 Arel 中重新表述。
UPDATE observations o, names n
SET o.lifeform = n.lifeform
WHERE o.name_id = n.id
AND o.lifeform != n.lifeform
在Arel,我写了这个:
names = Name.arel_table
obs = Observation.arel_table
join_source = Arel::Nodes::JoinSource.new(
obs, [obs.create_join(names)]
)
Arel::UpdateManager.new.
table(join_source).
where(obs[:id].eq(names[:id]).
and(obs[:lifeform].not_eq(names[:lifeform]))).
set([[obs[:lifeform], names[:lifeform]]])
这个returns:
Mysql2::Error: Column 'lifeform' in field list is ambiguous:
问题在最后。由此生成的SQL没有指定要设置列的table。
UPDATE `observations`
INNER JOIN `names`
SET `lifeform` = `names`.`lifeform`
WHERE (`observations`.`id` = `names`.`id`)
AND (`observations`.`lifeform` != `names`.`lifeform`)
在其他地方,Arel 生成的 SQL 通常使用 table 名称限定列以避免歧义。但是 source code for update_manager.rb definitely uses Nodes::UnqualifiedColumn.new(column)
. (I have added my description 到 GitHub 上的 Arel 问题。)
现在我可能想用其他方式重新表述我的 Arel。有没有办法强制 Arel 引用 table 名称,类似于 connection.quote_table_name
?
或者使用 CTE 是否合适?
我猜一种方法是使用 ActiveRecord 的 connection.update_all
。
names = Arel::Table.new(:names)
Observation.joins(:name).
where(names[:correct_spelling_id].not_eq(nil)).
update_all("`observations`.`name_id` = `names`.`correct_spelling_id`")
这会生成所需的 SQL:
UPDATE `observations`
INNER JOIN `names`
ON (`observations`.`name_id` = `names`.`correct_spelling_id`)
AND (`names`.`correct_spelling_id` IS NOT NULL)
SET `observations`.`name_id` = `names`.`correct_spelling_id`
我认为这是要走的路。
显然有一个 issue in Arel core,其中 Arel::UpdateManager
在对连接执行列更新时不会为更新列生成 table 名称。它导致无效 SQL.
我在 Rails 5.2 应用程序中 运行 加入了这个,其中我有一个 SQL 文字更新语句,我试图在 Arel 中重新表述。
UPDATE observations o, names n
SET o.lifeform = n.lifeform
WHERE o.name_id = n.id
AND o.lifeform != n.lifeform
在Arel,我写了这个:
names = Name.arel_table
obs = Observation.arel_table
join_source = Arel::Nodes::JoinSource.new(
obs, [obs.create_join(names)]
)
Arel::UpdateManager.new.
table(join_source).
where(obs[:id].eq(names[:id]).
and(obs[:lifeform].not_eq(names[:lifeform]))).
set([[obs[:lifeform], names[:lifeform]]])
这个returns:
Mysql2::Error: Column 'lifeform' in field list is ambiguous:
问题在最后。由此生成的SQL没有指定要设置列的table。
UPDATE `observations`
INNER JOIN `names`
SET `lifeform` = `names`.`lifeform`
WHERE (`observations`.`id` = `names`.`id`)
AND (`observations`.`lifeform` != `names`.`lifeform`)
在其他地方,Arel 生成的 SQL 通常使用 table 名称限定列以避免歧义。但是 source code for update_manager.rb definitely uses Nodes::UnqualifiedColumn.new(column)
. (I have added my description 到 GitHub 上的 Arel 问题。)
现在我可能想用其他方式重新表述我的 Arel。有没有办法强制 Arel 引用 table 名称,类似于 connection.quote_table_name
?
或者使用 CTE 是否合适?
我猜一种方法是使用 ActiveRecord 的 connection.update_all
。
names = Arel::Table.new(:names)
Observation.joins(:name).
where(names[:correct_spelling_id].not_eq(nil)).
update_all("`observations`.`name_id` = `names`.`correct_spelling_id`")
这会生成所需的 SQL:
UPDATE `observations`
INNER JOIN `names`
ON (`observations`.`name_id` = `names`.`correct_spelling_id`)
AND (`names`.`correct_spelling_id` IS NOT NULL)
SET `observations`.`name_id` = `names`.`correct_spelling_id`
我认为这是要走的路。