在Rails中使用计数器的模型实现原子插入/更新的最佳方式是什么?我想解决的一个很好的类比是一个“喜欢”计数器,有两个字段:
url : string count : integer
插入后,如果当前没有匹配的URL的记录,则应创建一个新的记录,其数量为1;否则现有记录的计数字段应该增加.
最初我尝试过如下代码:
Like.find_or_create_by_url("http://example.com").increment!(:count)
但不足为奇的是,生成的sql表明SELECT发生在UPDATE事务之外:
Like Load (0.4ms) SELECT `likes`.* FROM `likes` WHERE `likes`.`url` = 'http://example.com' LIMIT 1 (0.1ms) BEGIN (0.2ms) UPDATE `likes` SET `count` = 4,`updated_at` = '2013-01-17 19:41:22' WHERE `likes`.`id` = 2 (1.6ms) COMMIT
有没有Rails成语来处理这个问题,还是需要在sql级别实现这一点(从而失去一些可移植性)?
解决方法
我不知道在单个查询中实现原子增量的任何ActiveRecord方法.你自己的答案是远远可以得到的.
所以你的问题可能无法使用ActiveRecord来解决.记住:ActiveRecord只是一个映射器来简化一些事情,同时使其他人复杂化.一些问题只能通过纯SQL查询解析到数据库.你会松动一些可移植性.下面的例子将在MysqL中工作,但据我所知,不在sqllite和其他.
quoted_url = ActiveRecord::Base.connection.quote(@your_url_here) ::ActiveRecord::Base.connection.execute("INSERT INTO likes SET `count` = 1,url = \"#{quoted_url}\" ON DUPLICATE KEY UPDATE count = count+1;");