【问题标题】:rails mass update column by inserting value calculated from other columns通过插入从其他列计算的值来更新列
【发布时间】:2015-07-26 22:26:18
【问题描述】:

我正在 rails 中制作体育排行榜,并尝试使用从其他列计算的值批量分配一列。以下是列的名称::pts、:games_played 和 :pts_per_game。前两个已经分配了值,我想通过 将 :pts 除以 :games_played 将值插入到最后一个中。所以我去了rails控制台并尝试了

PlayerLeaderboard.update_all(pts_per_game: pts/games_played)

但是我用 NoMethodError 得到这个输出

NoMethodError: undefined method `pts' for main:Object
    from (irb):24
    from /Users/AYL/.rvm/rubies/ruby-2.1.5/lib/ruby/gems/2.1.0/gems/railties-4.2.0/lib/rails/commands/console.rb:110:in `start'
    from /Users/AYL/.rvm/rubies/ruby-2.1.5/lib/ruby/gems/2.1.0/gems/railties-4.2.0/lib/rails/commands/console.rb:9:in `start'
    from /Users/AYL/.rvm/rubies/ruby-2.1.5/lib/ruby/gems/2.1.0/gems/railties-4.2.0/lib/rails/commands/commands_tasks.rb:68:in `console'
    from /Users/AYL/.rvm/rubies/ruby-2.1.5/lib/ruby/gems/2.1.0/gems/railties-4.2.0/lib/rails/commands/commands_tasks.rb:39:in `run_command!'
    from /Users/AYL/.rvm/rubies/ruby-2.1.5/lib/ruby/gems/2.1.0/gems/railties-4.2.0/lib/rails/commands.rb:17:in `<top (required)>'
    from /Users/AYL/.rvm/rubies/ruby-2.1.5/lib/ruby/gems/2.1.0/gems/activesupport-4.2.0/lib/active_support/dependencies.rb:274:in `require'
    from /Users/AYL/.rvm/rubies/ruby-2.1.5/lib/ruby/gems/2.1.0/gems/activesupport-4.2.0/lib/active_support/dependencies.rb:274:in `block in require'
    from /Users/AYL/.rvm/rubies/ruby-2.1.5/lib/ruby/gems/2.1.0/gems/activesupport-4.2.0/lib/active_support/dependencies.rb:240:in `load_dependency'
    from /Users/AYL/.rvm/rubies/ruby-2.1.5/lib/ruby/gems/2.1.0/gems/activesupport-4.2.0/lib/active_support/dependencies.rb:274:in `require'
    from /Users/AYL/Dropbox/workspace/rails/krossover/bin/rails:8:in `<top (required)>'
    from /Users/AYL/.rvm/rubies/ruby-2.1.5/lib/ruby/gems/2.1.0/gems/activesupport-4.2.0/lib/active_support/dependencies.rb:268:in `load'
    from /Users/AYL/.rvm/rubies/ruby-2.1.5/lib/ruby/gems/2.1.0/gems/activesupport-4.2.0/lib/active_support/dependencies.rb:268:in `block in load'
    from /Users/AYL/.rvm/rubies/ruby-2.1.5/lib/ruby/gems/2.1.0/gems/activesupport-4.2.0/lib/active_support/dependencies.rb:240:in `load_dependency'
    from /Users/AYL/.rvm/rubies/ruby-2.1.5/lib/ruby/gems/2.1.0/gems/activesupport-4.2.0/lib/active_support/dependencies.rb:268:in `load'
    from /Users/AYL/.rvm/rubies/ruby-2.1.5/lib/ruby/gems/2.1.0/gems/spring-1.3.6/lib/spring/commands/rails.rb:6:in `call'
    from /Users/AYL/.rvm/rubies/ruby-2.1.5/lib/ruby/gems/2.1.0/gems/spring-1.3.6/lib/spring/command_wrapper.rb:38:in `call'
    from /Users/AYL/.rvm/rubies/ruby-2.1.5/lib/ruby/gems/2.1.0/gems/spring-1.3.6/lib/spring/application.rb:183:in `block in serve'
    from /Users/AYL/.rvm/rubies/ruby-2.1.5/lib/ruby/gems/2.1.0/gems/spring-1.3.6/lib/spring/application.rb:156:in `fork'
    from /Users/AYL/.rvm/rubies/ruby-2.1.5/lib/ruby/gems/2.1.0/gems/spring-1.3.6/lib/spring/application.rb:156:in `serve'
    from /Users/AYL/.rvm/rubies/ruby-2.1.5/lib/ruby/gems/2.1.0/gems/spring-1.3.6/lib/spring/application.rb:131:in `block in run'
    from /Users/AYL/.rvm/rubies/ruby-2.1.5/lib/ruby/gems/2.1.0/gems/spring-1.3.6/lib/spring/application.rb:125:in `loop'
    from /Users/AYL/.rvm/rubies/ruby-2.1.5/lib/ruby/gems/2.1.0/gems/spring-1.3.6/lib/spring/application.rb:125:in `run'
    from /Users/AYL/.rvm/rubies/ruby-2.1.5/lib/ruby/gems/2.1.0/gems/spring-1.3.6/lib/spring/application/boot.rb:18:in `<top (required)>'
    from /Users/AYL/.rvm/rubies/ruby-2.1.5/lib/ruby/site_ruby/2.1.0/rubygems/core_ext/kernel_require.rb:54:in `require'
    from /Users/AYL/.rvm/rubies/ruby-2.1.5/lib/ruby/site_ruby/2.1.0/rubygems/core_ext/kernel_require.rb:54:in `require'
    from -e:1:in `<main>'irb(main):025:0>

奇怪的是,我以前尝试过这种方法,它奏效了。我刚刚将我的开发数据库从 sqlite3 切换到 postgres 并重新播种数据。在我看来,切换数据库不太可能是导致此错误的原因;我一定错过了什么。
我也试过update_column。

提前致谢!

【问题讨论】:

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


    【解决方案1】:

    该代码将pts 和games_played 视为self 上的局部变量或方法。没有这样的方法,所以报错。

    您需要列,因此您需要将一些 SQL 发送到 update_all 而不是 Ruby 哈希:

    PlayerLeaderboard.update_all('pts_per_game = pts / games_played')
    

    大概pts 和games_played 是整数列,所以你会在这里遇到截断问题。如果这就是你想要的(即5 / 3 是1),那么就没有什么好担心的了。如果您的 pts_per_game 是浮点数,那么您需要在混合中添加一个演员:

    PlayerLeaderboard.update_all('pts_per_game = cast(pts as float) / games_played')
    PlayerLeaderboard.update_all('pts_per_game = pts::float / games_played')
    

    cast 是标准 SQL,:: 语法是 PostgreSQL 特有的。

    如果您的 pts_per_game 是一个整数并且您希望它四舍五入,那么您必须这样说:

    PlayerLeaderboard.update_all('pts_per_game = round(cast(pts as float) / games_played)')
    

    如果可能出现负值,那么您需要使用ceil 或floor 而不是round 来选择负值的四舍五入方向。

    如果您在games_played 中有零,那么您需要进行两次更新并决定在没有任何游戏时pts_per_game 应该是什么,我想零和 NULL 将是合理的选择:

    PlayerLeaderboard.where(:games_played => 0).update_all(...)
    PlayerLeaderboard.where.not(:games_played => 0).update_all(:pts_per_game => 0)
    

    如果您在没有任何游戏时为 pts_per_game 使用 NULL,那么您只需要第一个 update_all,因为新列默认为 NULL。

    【讨论】:

    • 如何避免ActiveRecord::StatementInvalid: PG::DivisionByZero: ERROR: division by zero 错误?
    【解决方案2】:

    您可以使用 ActiveRecord 进行这样的操作:

    PlayerLeaderboard.update_all("pts_per_game = pts/games_played")
    

    【讨论】:

      猜你喜欢
      • 2015-06-02
      • 1970-01-01
      • 2020-12-12
      • 1970-01-01
      • 2019-11-11
      • 1970-01-01
      • 1970-01-01
      • 2019-06-28
      • 2017-07-23
      相关资源
      最近更新 更多