我正在尝试编写(Ruby)脚本,该脚本将删除PostgreSQL DB中的所有外键和唯一约束,然后重新添加它们。
FK部分似乎工作正常。
但是,删除和重新创建唯一约束不起作用。
我认为原因是当创建唯一约束时,PostgreSQL会随之创建索引,并且在删除唯一约束时不会自动删除该索引。那么当脚本尝试重新添加唯一约束时,我会收到类似......
的错误PG::Error: ERROR: relation "unique_username" already exists
: ALTER TABLE users ADD CONSTRAINT unique_username UNIQUE (username)
事实上,当我查看pgAdmin GUI实用程序中的数据库时,该索引存在。
问题是,如何在我的脚本中找到它并放弃它?
这是我的剧本......
manage_constraints.rake
namespace :journal_app do
desc 'Drop constraints'
task :constraints_drop => :environment do
sql = %Q|
SELECT
constraint_name, table_catalog, table_name
FROM
information_schema.table_constraints
WHERE
table_catalog = 'journal_app_#{Rails.env}'
AND
constraint_name NOT LIKE '%_pkey'
AND
constraint_name NOT LIKE '%_not_null';
|
results = execute_sql(sql)
results.each do |row|
puts "Dropping constraint #{row['constraint_name']} from table #{row['table_name']}."
execute_sql("ALTER TABLE #{row['table_name']} DROP CONSTRAINT #{row['constraint_name']}")
end
end
# --------------------------------------------------------------------------------------------------------------------
desc 'Drops constraints, then adds them'
task :constraints_add => :environment do
Rake::Task['journal_app:constraints_drop'].invoke
UNIQUE_KEYS = [
{
:name => 'unique_username',
:table => 'users',
:columns => ['username']
},
{
:name => 'unique_email',
:table => 'users',
:columns => ['email']
}
]
FKs = [
{
:name => 'fk_entries_users',
:parent_table => 'users',
:child_table => 'entries',
:on_delete => 'CASCADE'
},
{
:name => 'fk_entries_entry_tags',
:parent_table => 'entries',
:child_table => 'entry_tags',
:on_delete => 'CASCADE'
},
# etc...
]
UNIQUE_KEYS.each do |constraint|
sql = "ALTER TABLE #{constraint[:table]} ADD CONSTRAINT #{constraint[:name]} UNIQUE (#{constraint[:columns].join(', ')})"
puts "Adding unique constraint #{constraint[:name]} to table #{constraint[:table]}."
puts ' SQL:'
puts " #{sql}"
execute_sql(sql)
end
FKs.each do |fk|
sql = %Q|
ALTER TABLE #{fk[:child_table]} ADD CONSTRAINT #{fk[:name]} FOREIGN KEY (#{fk[:parent_table].singularize}_id)
REFERENCES #{fk[:parent_table]} (id)
ON UPDATE NO ACTION ON DELETE #{fk[:on_delete]}|.strip!
puts "Adding foreign key #{fk[:name]}."
puts ' SQL:'
puts " #{sql}"
execute_sql(sql)
end
end
end
def execute_sql(sql)
ActiveRecord::Base.connection.execute(sql)
end
答案 0 :(得分:1)
首先,为什么会这样做?这有一种感觉“我已经决定解决方案Y问题X,并且我遇到问题解决方案Y我正在询问” - 其中真正的答案是“使用解决方案Z而不是解决方案Y来解决问题X“。换句话说,尝试解释您遇到的潜在问题,可能有更好的方法来解决它。
如果必须这样做,请查询pg_catalog.pg_index inner join pg_class on pg_class.oid = pg_index.indexrelid
以查找不 indisprimary
的索引,并排除任何EXISTS (SELECT 1 FROM pg_constraint on pg_index.indrelid = pg_constraint.conindid)
的索引。
例如:
SELECT pg_class.relname
FROM pg_index INNER JOIN pg_class ON (pg_class.oid = pg_index.indexrelid)
INNER JOIN pg_namespace ON (pg_class.relnamespace = pg_namespace.oid)
WHERE NOT EXISTS (
SELECT 1 FROM pg_constraint WHERE pg_index.indrelid = pg_constraint.conindid
)
AND pg_index.indisprimary = 'f'
AND pg_namespace.nspname NOT LIKE 'pg_%';
请注意,此类查询可能会在任何主要版本转换中中断,因为pg_catalog
无法保证跨版本保留相同的架构。查询Pg版本并在必要时使用特定于版本的查询。听起来很痛苦?它是,但通常不应该是必要的,你只是做一些奇怪的事情。
在大多数情况下,非常稳定的information_schema
就足够了。