Rails:根据同级记录创建唯一的自动增量ID

时间:2019-06-11 05:07:03

标签: ruby-on-rails ruby database

我的Rails项目中有三个模型,分别是用户,游戏,比赛

用户可以在每个游戏上创建许多比赛 所以匹配的表结构就像

            table name: game_matches

+----+---------+---------+-------------+------------+
| id | user_id | game_id | match_type  | match_name |
+----+---------+---------+-------------+------------+
|  1 |       1 |       1 | practice    |            |
|  2 |       3 |       2 | challenge   |            |
|  3 |       1 |       1 | practice    |            |
|  4 |       3 |       2 | challenge   |            |
|  5 |       1 |       1 | challenge   |            |
|  6 |       3 |       2 | practice    |            |
+----+---------+---------+-------------+------------+

我想根据user_id,game_id和match_type值生成比赛名称

for example match_name should be create like below


+----+---------+---------+-------------+-------------+
| id | user_id | game_id | match_type  | match_name  |
+----+---------+---------+-------------+-------------+
|  1 |       1 |       1 | practice    | Practice 1  |
|  2 |       3 |       2 | challenge   | Challenge 1 |
|  3 |       1 |       1 | practice    | Practice 2  |
|  4 |       3 |       2 | challenge   | Challenge 2 |
|  5 |       1 |       1 | challenge   | Challenge 1 |
|  6 |       3 |       2 | practice    | Practice 1  |
+----+---------+---------+-------------+-------------+

在创建新记录期间,如何在Rails模型中实现此自动增量值。 任何帮助建议表示赞赏。

谢谢。

2 个答案:

答案 0 :(得分:5)

我看到两种解决方法:

  • 数据库:触发器
  • 路轨:回调

触发(假设Postgres):

  DROP TRIGGER IF EXISTS trigger_add_match_name ON customers;
  DROP FUNCTION IF EXISTS function_add_match_name();
  CREATE FUNCTION function_add_match_name()
  RETURNS trigger AS $$
  BEGIN
    NEW.match_name := (
      SELECT
      CONCAT(game_matches.match_type, ' ', COALESCE(count(*), 0))
      FROM game_matches
      WHERE game_matches.user_id = NEW.user_id AND game_matches.match_type = NEW.match_type
    );
    RETURN NEW;
  END
  $$ LANGUAGE 'plpgsql';

  CREATE TRIGGER trigger_add_match_name
  BEFORE INSERT ON game_matches
  FOR EACH ROW
  EXECUTE PROCEDURE function_add_match_name(); 

请注意,这未经测试。

铁路

class GameMatch
  before_create :assign_match_name

  private

  def assign_match_name
   number = GameMatch.where(user_id: user_id, match_type: match_type).count || 0
   name = "#{match_type} #{number + 1}"
   self.match_name = name
  end
end

再次,未经测试。

我希望使用触发器解决方案,因为通过纯SQL插入时可以完全跳过或忽略回调。

我还要添加“ match_number”列而不是全名,然后在“模型”或“装饰器”或“视图助手”(更灵活,I18n)中构造名称,但背后的逻辑保持不变。

答案 1 :(得分:0)

您应该为这两个match_name user检索最后一个game,将其拆分,增加计数器,并加一个空格。不幸的是,SQL不提供SPLIT函数,因此如下所示是一个不错的开始:

SELECT match_name
FROM match_name
WHERE user_id = 3
  AND game_id = 2
ORDER BY id DESC
LIMIT 1

实际上,我最好创建类型为match_number的{​​{1}}列,以按类型保留数字,并且通过将类型与该数字连接来产生名称。