在Rails中对两列上的索引和该对上的唯一约束分开

时间:2012-12-06 22:11:17

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

我的应用使用PostgreSQL数据库。我有一个看起来像这样的迁移:

class CreateTagAssignments < ActiveRecord::Migration
  def change
    create_table :tag_assignments do |t|
      t.integer :tag_id
      t.integer :quote_id
      t.integer :user_id

      t.timestamps
    end

    add_index :tag_assignments, :tag_id
    add_index :tag_assignments, :quote_id
  end
end

这两列经常会搜索记录,所以我希望每个列都有一个单独的索引。但现在我想在数据库级别强制执行该对(tag_idquote_id)的唯一性。我试过了add_index :tag_assignments, [:tag_id, :quote_id], unique: true,但我得到了错误:

PG::Error: ERROR:  could not create unique index "index_tag_assignments_on_tag_id_and_quote_id"
DETAIL:  Key (tag_id, quote_id)=(10, 1) is duplicated.
: CREATE UNIQUE INDEX "index_tag_assignments_on_tag_id_and_quote_id" ON "tag_assignments" ("tag_id", "quote_id")

因此多个索引显然可以完成多列索引的工作吗?如果是这样,那么我可以使用ALTER TABLE ... ADD CONSTRAINT添加约束,但是如何在ActiveRecord中执行?

编辑:手动执行ALTER TABLE ... ADD CONSTRAINT会产生相同的错误。

2 个答案:

答案 0 :(得分:4)

正如Erwin所指出的,“Key(tag_id,quote_id)=(10,1)是重复的”约束违规错误消息告诉您现有数据已经​​违反了您的唯一约束。我从模型的可见内容推断出不同的用户可以在标签和引用之间引入公共关联,因此当您尝试仅限制quote_id,tag_id对的唯一性时,您会看到重复项。复合索引对于前导键的索引访问仍然有用(虽然效率略低于单个列索引,因为复合索引的密钥密度较低)。您可能可以获得所需的速度以及具有两个索引的相应唯一约束,其中一个ID上的单个列索引和所有三个ID上的复合索引,另一个id作为其前导字段。如果从tag到quote的映射是一种比从quote到tag映射更频繁的访问路径,我会尝试这样做:

add_index :tag_assignments, :tag_id
add_index :tag_assignments, [:quote_id,:tag_id,:user_id], unique: true

如果您使用的是Pg&gt; = 9.2,则可以利用9.2的索引可见性映射来启用covering indexes的仅索引扫描。在这种情况下,使上面的第一个索引包含所有三个id可能会有好处,tag_id和quote_id领先:

add_index :tag_assignments, [:tag_id,:quote_id,user_id]

目前还不清楚user_id是如何约束您的查询的,所以您可能会发现您希望索引的位置也是先前提升的。

答案 1 :(得分:2)

  

因此多个索引显然可以完成多列索引的工作吗?

这个结论在你描述之后是不真实的,也是没有根据的。错误消息表明相反。多列上的multicolumn indexUNIQUE约束(实现多列索引)也提供了无法从多个单列索引中获取的功能。