2012-09-26 887 views
0

我想寫(Ruby)腳本,將所有外鍵和唯一約束放在我的PostgreSQL數據庫,然後重新添加它們。如何從PostgreSQL數據庫中刪除唯一約束?

FK部分似乎工作正常。

但是,刪除並重新創建唯一約束不起作用。

我認爲原因是當創建唯一約束時,PostgreSQL會隨之創建一個索引,並且在刪除唯一約束時該索引不會自動刪除。當腳本嘗試重新添加唯一約束那麼,我得到這樣一個錯誤......

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

事實上,當我看DB在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

唯一約束和唯一索引之間沒有區別。爲什麼不開始在psql命令行上首先執行查詢,而不是添加一層潛在的混淆。另外請注意,約束和索引名稱不是本地的表,這就是爲什麼當你讓db爲你選擇一個默認名稱時,它們通常以表名開頭。 –

+0

「*當唯一約束被刪除時,該索引不會自動刪除*」。那是錯的。刪除唯一約束時,會刪除索引***。你可能不會「下降」,因此pgAdmin仍然顯示它? –

回答

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就足夠了。

+0

感謝您的回答。是的,這聽起來很痛苦。爲什麼要做這樣的事情?原因是我想編寫一個腳本來維護約束,而不是使用psql或其他客戶端逐個應用它們。這樣我就可以將所有約束條件列在一個文件中,我可以在開發和生產數據庫中保持它們的一致性,並且可以將腳本保留在版本控制中。 – Ethan

+0

@Ethan查看數據庫DDL版本控制技術,如[Post Facto](https://www.pgcon.org/2009/schedule/events/158.en。HTML)以及可以讓整個數據庫定義(包括表格,約束等)保留在版本描述符中的系統。 Rails不會爲遷移而爲你做這件事嗎?或者軌道不理解約束? –

+0

感謝您的信息。是的,我不認爲Rails遷移支持FK約束。 – Ethan