【问题标题】:How to `update_all` based on a subquery in Rails如何基于 Rails 中的子查询`update_all`
【发布时间】:2018-09-06 13:54:37
【问题描述】:

我正在尝试以 Rails 方式在 PostgreSQL 中实现一些非常简单的事情。

假设您有一个 User 模型,它有 3 列 idcached_segmentcached_step。 假设您已经有一个复杂的查询,可以即时计算segmentquery,并封装在User.decorate_with_segment_and_step 范围内。这将返回一个 ActiveRecord 关系,与 User 相同,但有 3 个附加列:

id  cached_segment cached_step segment    step    cache_invalid
1   NULL           NULL        segment_1  step_1  TRUE
2   segment_1      step_2      segment_1  step_2  FALSE
3   ...

我想生成的 SQL 如下(PostgreSQL 风格):

UPDATE users
SET cached_segment = segment_1
    cached_step    = step
FROM (#{User.decorate_with_segment_and_step.to_sql}) decorated_users
WHERE decorated_users.cache_invalid AND decorated_users.id = users.id

理想情况下,我可以做类似的事情

User.
  from(User.decorate_with_segment_and_step, 'decorated_users').
  where(cache_invalid: true).
  update_all('cached_segment = segment, cached_step = step')

我对上面的语句没有运气,update_all,根据源代码,在构建更新语句时简单地丢弃了from 子句。

注意:我知道我可以使用User.connection.execute(my_raw_sql_here),这就是我现在正在做的事情。目标是坚持使用 ActiveRecord。

【问题讨论】:

  • ActiveRecord 有其局限性,这很好,因为使用另一层复杂性(ActiveRecord 或 Arel 表)执行复杂的 SQL 操作无济于事......
  • 你试过User.decorate_with_segment_and_step.where(cache_invalid: true).update_all('cached_segment = segment, cached_step = step')吗?
  • 是的,遗憾的是,更新子句中似乎只能使用实际的数据库列

标签: ruby-on-rails rails-activerecord bulkupdate


【解决方案1】:

是的,AR 有其局限性,但这不是其中之一。假设你在所有事情上都使用 Postgres——开发、测试和生产——你这样做的咒语是:

ActiveRecord::Base.connection.execute("UPDATE users SET cached_segment = segment_1, cached_step = step FROM (#{User.decorate_with_segment_and_step.to_sql}) decorated_users WHERE decorated_users.cache_invalid AND decorated_users.id = users.id")

希望对你有帮助...

【讨论】:

    猜你喜欢
    • 2015-04-20
    • 1970-01-01
    • 2019-10-20
    • 2016-05-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多