web-dev-qa-db-ja.com

Postgres:制約がまだ存在しない場合は追加します

PostgresにはALTER TABLE foo ADD CONSTRAINT bar ...制約が既に存在する場合、エラーを発生させないようにコマンドを無視しますか?

51

これは役立つかもしれませんが、少し汚いハックかもしれません:

create or replace function create_constraint_if_not_exists (
    t_name text, c_name text, constraint_sql text
) 
returns void AS
$$
begin
    -- Look for our constraint
    if not exists (select constraint_name 
                   from information_schema.constraint_column_usage 
                   where table_name = t_name  and constraint_name = c_name) then
        execute constraint_sql;
    end if;
end;
$$ language 'plpgsql'

次に呼び出します:

SELECT create_constraint_if_not_exists(
        'foo',
        'bar',
        'ALTER TABLE foo ADD CONSTRAINT bar CHECK (foobies < 100);')

更新:

Webmutの答え 以下のように提案:

ALTER TABLE foo DROP CONSTRAINT IF EXISTS bar;
ALTER TABLE foo ADD CONSTRAINT bar ...;

開発データベースではおそらく問題ありません。または、メンテナンスウィンドウをこのデータベースに依存しているアプリをシャットダウンできることがわかっている場合は、おそらく問題ありません。

しかし、これが24時間365日稼働するミッションクリティカルな実稼働環境である場合、このように無制限に制約を削除することは望ましくありません。数ミリ秒でも、誤った値がすり抜けられる可能性のある制約を強制しなくなった短いウィンドウがあります。これは、将来のある時点でかなりのビジネスコストにつながる意図しない結果をもたらす可能性があります。

32
Kev

可能な解決策は、新しい制約を作成する前にDROP IF EXISTSを使用するだけです。

ALTER TABLE foo DROP CONSTRAINT IF EXISTS bar;
ALTER TABLE foo ADD CONSTRAINT bar ...;

Information_schemaまたはカタログを照会するよりも簡単に見えますが、常に制約を再作成するため、巨大なテーブルでは時間がかかる場合があります。

編集2015-07-13:Kevは his answer で、制約が存在せず強制されていない場合にソリューションが短いウィンドウを作成することを指摘しました。これは事実ですが、トランザクションで両方のステートメントをラップすることにより、このようなウィンドウを非常に簡単に回避できます。

62
Webmut

匿名DOブロック内で例外ハンドラーを使用して、オブジェクトの重複エラーをキャッチできます。

DO $$
BEGIN

  BEGIN
    ALTER TABLE foo ADD CONSTRAINT bar ... ;
  EXCEPTION
    WHEN duplicate_object THEN RAISE NOTICE 'Table constraint foo.bar already exists';
  END;

END $$;

http://www.postgresql.org/docs/9.4/static/sql-do.htmlhttp://www.postgresql.org/docs/9.4/static/plpgsql- control-structures.htmlhttp://www.postgresql.org/docs/9.4/static/errcodes-appendix.html

18
Mike Stankavich

pg_constraint tableに対してクエリを実行して、制約の有無を確認できます。

SELECT 1 FROM pg_constraint WHERE conname = 'constraint_name'"
9
Behnam

制約を作成することは、大量のデータを含むテーブルに対してコストのかかる操作になる可能性があるため、すぐに再作成するためだけに制約を削除しないことをお勧めします。

Mike Stankavichと非常によく似た匿名コードブロックを使用してこれを解決することを選択しましたが、Mike(エラーをキャッチする)とは異なり、まず制約が存在するかどうかを確認します。

DO $$
BEGIN
    IF NOT EXISTS ( SELECT  constraint_schema
                ,       constraint_name 
                FROM    information_schema.check_constraints 
                WHERE   constraint_schema = 'myschema'
                  AND   constraint_name = 'myconstraintname'
              )
    THEN
        ALTER TABLE myschema.mytable ADD CONSTRAINT myconstraintname CHECK (column <= 100);
    END IF;
END$$; 
6
jamiet