我的应用程序使用 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
会产生相同的错误。
最佳答案
正如 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 的索引可见性映射来启用对 covering indexes 的仅索引扫描。 .在这种情况下,让上面的第一个索引包含所有三个 id,以 tag_id 和 quote_id 开头可能会有好处:
add_index :tag_assignments, [:tag_id,:quote_id,user_id]
尚不清楚 user_id 如何限制您的查询,因此您可能会发现您还希望索引的位置也提早提升。
关于ruby-on-rails - 两列上的单独索引和 Rails 中对的唯一约束,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13753551/