【问题标题】:How can I remove unique constraints from a PostgreSQL DB?如何从 PostgreSQL 数据库中删除唯一约束?
【发布时间】:2012-09-17 14:16:30
【问题描述】:

我正在尝试编写 (Ruby) 脚本,该脚本将删除我的 PostgreSQL 数据库中的所有外键和唯一约束,然后重新添加它们。

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

【问题讨论】:

  • 唯一约束和唯一索引之间没有区别。为什么不先在 psql 命令行上开始执行查询,而不是添加一层潜在的混淆。另外,请注意,约束和索引名称不是表的本地名称,这就是为什么当您让 db 为您选择默认名称时,它们通常以表名称开头。
  • 删除唯一约束时,该索引不会自动删除”。那是错的。当您删除唯一约束时,索引 is 会被删除。你可能不是 commit 下降,因此 pgAdmin 仍在显示它?

标签: ruby-on-rails postgresql


【解决方案1】:

首先,为什么要这样做?这有一种“我已经决定解决问题 X 的 Y 方案,并且我正在询问的解决方案 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 就足够了。

【讨论】:

  • 感谢您的回答。是的,这听起来很痛苦。为什么要做这样的事情?原因是我想编写一个脚本来维护约束,而不是使用 psql 或其他客户端一个一个地应用它们。这样我就可以将所有约束都列在一个文件中,我可以在开发和生产数据库中保持它们的一致性,并且我可以将脚本保持在版本控制中。
  • @Ethan 查看数据库 DDL 版本控制技术,如 Post Facto,以及允许您将整个数据库定义(包括表、约束等)保存在版本化描述符中的系统。 Rails 不会通过迁移为您做到这一点吗?还是 rails 不理解约束?
  • 感谢您的信息。是的,我不认为 Rails 迁移支持 FK 约束。
  • @Ethan 看起来不是直接的,但您仍然可以在迁移中使用一些手写 SQL 来完成。 stackoverflow.com/questions/4664520/…
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-07-26
  • 1970-01-01
  • 2023-03-22
  • 2021-08-03
  • 2012-09-02
  • 2023-04-06
相关资源
最近更新 更多