0

我的应用程序使用 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_id, ) 的唯一性。quote_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会产生相同的错误。

4

2 回答 2

4

正如 Erwin 指出的那样,“Key (tag_id, quote_id)=(10, 1) is duplicated”约束违规错误消息告诉您,您的唯一约束已经被现有数据违反。我从您的模型的可见内容推断出不同的用户可以在标签和引用之间引入一个共同的关联,因此当您尝试仅限制 quote_id,tag_id 对的唯一性时,您会看到重复。复合索引对于前导键的索引访问仍然有用(尽管效率略低于单列索引,因为复合索引的键密度较低)。您可能会获得所需的速度以及具有两个索引的适当唯一约束,其中一个 id 上的单列索引和所有三个 id 上的复合索引,另一个 id 作为其前导字段。

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

如果您使用 Pg >= 9.2,则可以利用 9.2 的索引可见性映射来启用覆盖索引的仅索引扫描。在这种情况下,使上面的第一个索引包含所有三个 id 可能会有好处,其中 tag_id 和 quote_id 前导:

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

尚不清楚 user_id 如何约束您的查询,因此您可能会发现您希望索引的位置也更早提升。

于 2012-12-07T00:16:40.987 回答
2

那么多个索引显然可以完成多列索引的工作?

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

于 2012-12-06T22:27:46.663 回答