0

PostgreSQL DB内のすべての外部キーと一意の制約を削除してからそれらを再度追加する(Ruby)スクリプトを作成しようとしています。

FK部分は正常に動作しているようです。

ただし、一意の制約を削除して再作成することはできません。

その理由は、一意の制約が作成されると、PostgreSQLがそれと一緒にインデックスを作成し、一意の制約が削除されてもそのインデックスが自動的に削除されないためだと思います。したがって、スクリプトが一意の制約を再度追加しようとすると、次のようなエラーが発生します...

PG::Error: ERROR:  relation "unique_username" already exists
: ALTER TABLE users ADD CONSTRAINT unique_username UNIQUE (username)

実際、pgAdmin GUIユーティリティでDBを見ると、そのインデックスが存在します。

問題は、スクリプトでそれを見つけてドロップするにはどうすればよいですか?


これが私のスクリプトです...

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
4

1 に答える 1

1

まず、なぜそんなことをするのですか?これは、「問題Xの解決策Yを決定し、私が求めている解決策Yに問題がある」というものの1つの感覚を持っています。実際の答えは「解決策Yではなく解決策Zを使用して解決する」です。問題X」。言い換えれば、あなたが抱えている根本的な問題を説明してみてください。それを解決するためのはるかに良い方法があるかもしれません。

必要な場合は、そうでないpg_catalog.pg_index inner join pg_class on pg_class.oid = pg_index.indexrelidインデックスをクエリし、。で何かを除外します。 indisprimaryEXISTS (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ていれば十分です。

于 2012-09-26T12:41:50.377 に答える