如何在 Rails 迁移中使用 GREATEST 和 LEAST?

How can I use GREATEST and LEAST in a Rails migration?

提问人:Steven 提问时间:10/12/2017 更新时间:11/17/2023 访问量:204

问:

我需要确保列和 上的表中的双向唯一性。friendshipsrequester_idaccepter_id

Table: friendships

requester_id | accepter_id
--------------------------
           1 |           2
           2 |           1  <-- this should not be possible

作为要考虑的解决方案,我找到了这种方法:Postgresql 强制执行唯一的列双向组合

create unique index ifriendz on friendz(greatest(from_id,to_id), least(from_id,to_id));

我正在尝试编写一个 Rails 迁移(我知道我也可以使用普通 SQL,但我想让它干净)。

这是我重写命令的尝试。这是行不通的。

class AddBidirectionalUniqueConstraintToFriendships < ActiveRecord::Migration
  def change
    add index :friendships, greatest[:requester_id, :accepter_id], least[:requester_id, :accepter_id], unique: true
  end
end
Ruby-on-Rails(英语:SQL RUBY-ON-RAILS)

评论

0赞 Tim Biegeleisen 10/12/2017
我对 Rails ActiveRecord 一无所知,但如果它像大多数其他 ORM 框架一样,那么它可能仅限于大部分 ANSI 风格的 SQL 功能。和函数在很大程度上是特定于数据库的。Postgres 和 MySQL 支持它,但不是所有数据库都支持它。因此,您尝试做的事情可能是不可能的。greatestleast

答:

4赞 Rob Di Marco 10/12/2017 #1

您可以使用命令运行任意 SQL 命令。execute

例如:

class AddBidirectionalUniqueConstraintToFriendships < ActiveRecord::Migration
  def up
    execute <<-SQL
      create unique index ifriendz on friendz(greatest(from_id,to_id), least(from_id,to_id));
    SQL
  end

  def down
    execute <<-SQL
      drop index ifriendz;
    SQL
  end
end

```

评论

0赞 Steven 10/13/2017
谢谢。考虑到这可能仅适用于 MySQL 和 PostgreSQL,这是否干净?
0赞 mu is too short 10/13/2017
这还不够。 不会包含正确的索引,除非您还切换到 .db/schema.rbdb/structure.sql
0赞 Rob Di Marco 10/13/2017
mu 是正确的,在您的config/application.rbconfig.active_record.schema_format = :sqldb/structure.sql
0赞 aaronkelton 11/17/2023 #2

你实际上是在问如何防止双向唯一性。使用 GREATEST 和 LEAST 只是一种方法。相反,您可以编写自定义验证:

class Friendship
  validate :bidirectional_uniqueness

  def bidirectional_uniqueness
    if Friendship.where(requester_id: accepter_id, accepter_id: requester_id).present?
      errors.add(:requester_id, "is not bidirectionally unique with accepter_id")
    end
  end
end