PostgreSQLに存在しない場合、列を追加するにはどうすればよいですか?


145

質問は簡単です。xテーブルyに列を追加する方法は、x列が存在しない場合のみですか?私はここに列が存在するかどうかを確認する方法のみを見つけました。

SELECT column_name 
FROM information_schema.columns 
WHERE table_name='x' and column_name='y';

回答:


133

これは、「DO」ステートメントを使用した簡潔なバージョンです。

DO $$ 
    BEGIN
        BEGIN
            ALTER TABLE <table_name> ADD COLUMN <column_name> <column_type>;
        EXCEPTION
            WHEN duplicate_column THEN RAISE NOTICE 'column <column_name> already exists in <table_name>.';
        END;
    END;
$$

これらをパラメーターとして渡すことはできません。クライアント側で文字列の変数置換を行う必要がありますが、これは自己完結型のクエリであり、列が既に存在する場合にのみメッセージを発行し、存在しない場合は追加します。他のエラー(無効なデータ型など)で失敗し続けます。

これらが外部ソースからのランダムな文字列である場合、これらのメソッドのいずれかを実行することはお勧めしません。どの方法(クエリとして実行されるクレインサイドまたはサーバーサイドの動的文字列)を使用する場合でも、SQLインジェクション攻撃にさらされるため、これは災害のレシピになります。


4
DO $$ BEGIN BEGIN CREATE INDEX type_idx ON table1 USING btree (type); EXCEPTION WHEN duplicate_table THEN RAISE NOTICE 'Index exists.'; END; END;$$;同じ方法でCREATE INDEX;)ありがとうございます
marioosh

匿名コードブロックを開始するだけでDO $$失敗する理由がわかりません。私もdev postgres docsの例で示されているDO $$;ブロックを開始するまで失敗しDO $$DECLARE r record;ました。
nemesisfixx 2013

9
で閉じるとEND; $$構文エラーが発生し(Postgres 9.3)、END $$;代わりに使用しなければなり
ませんでした

5
良いアプローチですが、入れ子になったBEGIN / ENDブロックがなぜですか?それは私にとっては単一のレイヤーでうまく動作します。また、最後にセミコロン($$;)を追加すると、psqlのステートメントが明確になります。
シェーン

1
このアプローチ(EXCEPTION)は少し一般的で、IF NOT EXISTS構文のないタスク(たとえば)に使用できますALTER TABLE ... ADD CONSTRAINT
Tomasz Gandor

390

Postgres 9.6このオプションを使用して行うことができますif not exists

ALTER TABLE table_name ADD COLUMN IF NOT EXISTS column_name INTEGER;

4
甘い。残念ながらADD CONSTRAINT IF NOT EXISTSまだありません。
Tomasz Gandor

4
この答えがページの下部にあるのはなぜですか、他のオプションよりもはるかに優れています。
Ecksters

好奇心から抜け出します:これはテーブルにアクセスロックを引き起こしますか(そして、本番データベースの巨大なテーブルで実行するときにメンテナンスウィンドウが必要になりますか)?
Hassan Baig

4
Stack-overflowは、受け入れられた回答の変更を実際にサポートする必要があります。
Henrik Sommerland

@HenrikSommerland:それ許可されています-しかし、質問をした人だけ許可します。
a_horse_with_no_name

22
CREATE OR REPLACE function f_add_col(_tbl regclass, _col  text, _type regtype)
  RETURNS bool AS
$func$
BEGIN
   IF EXISTS (SELECT 1 FROM pg_attribute
              WHERE  attrelid = _tbl
              AND    attname = _col
              AND    NOT attisdropped) THEN
      RETURN FALSE;
   ELSE
      EXECUTE format('ALTER TABLE %s ADD COLUMN %I %s', _tbl, _col, _type);
      RETURN TRUE;
   END IF;
END
$func$  LANGUAGE plpgsql;

コール:

SELECT f_add_col('public.kat', 'pfad1', 'int');

TRUE成功した場合は戻りますFALSE(それ以外の場合は列が既に存在します)。
無効なテーブルまたは型名の例外を発生させます。

なぜ別のバージョンですか?

  • これはDOステートメントで実行できますが、DOステートメントは何も返すことができません。そしてそれが繰り返し使用される場合は、関数を作成します。

  • 私は、使用オブジェクト識別子タイプ regclassregtypeするため_tbl_type)は直ちに(安い可能な方法)の両方のSQLインジェクションおよびb)のチェックの有効性を防止します。列名_colは、で引き続き無害化する必要EXECUTEがありquote_ident()ます。この関連する回答の詳細説明:

  • format()Postgres 9.1以降が必要です。古いバージョンでは手動で連結します:

    EXECUTE 'ALTER TABLE ' || _tbl || ' ADD COLUMN ' || quote_ident(_col) || ' ' || _type;
  • テーブル名をスキーマで修飾できますが、必須ではありません。
    関数呼び出しで識別子を二重引用符で囲んで、キャメルケースと予約語を保持することができます(ただし、このいずれも使用しないでください)。

  • pg_catalog代わりにクエリを実行しますinformation_schema。詳細な説明:

  • 現在受け入れられている回答のEXCEPTIONような句を含むブロックは、かなり低速です。これは通常、より単純で高速です。ドキュメント:

ヒント:EXCEPTION句を含むブロックは、句を含まないブロックよりも、出入りするのに非常にコストがかかります。したがって、必要がない場合は使用しないでくださいEXCEPTION


私は私のソリューションよりもあなたのソリューションが好きです!それはより良く、より安全でより速いです。
デビッドS

私が使用する必要があるPostgresのバージョンにはDOステートメントがありません。受け入れるためのわずかな変更でDEFAULT、これは完全に機能しました!
リネーム

18

次の選択クエリはtrue/falseEXISTS()関数を使用してを返します。

EXISTS()EXISTS
の引数は、任意のSELECTステートメントまたはサブクエリです。サブクエリは、行を返すかどうかを判断するために評価されます。少なくとも1つの行を返す場合、EXISTSの結果は「true」です。サブクエリが行を返さない場合、EXISTSの結果は "false"です。

SELECT EXISTS(SELECT  column_name 
                FROM  information_schema.columns 
               WHERE  table_schema = 'public' 
                 AND  table_name = 'x' 
                 AND  column_name = 'y'); 

次の動的SQLステートメントを使用してテーブルを変更します

DO
$$
BEGIN
IF NOT EXISTS (SELECT column_name 
                 FROM  information_schema.columns 
                WHERE  table_schema = 'public' 
                  AND  table_name = 'x' 
                  AND  column_name = 'y') THEN
ALTER TABLE x ADD COLUMN y int DEFAULT NULL;
ELSE
RAISE NOTICE 'Already exists';
END IF;
END
$$

2
重複するテーブル名と列名は、複数のスキーマに存在できます。
マイクシェリル「キャットリコール」

1
まあ、あなたはあなたのコードを書き直してスキーマを説明したいと思うかもしれません。
Mike Sherrill「Cat Recall」、

2

Postgre 9.5+(ほとんどの人がそうだと思います)を使用している人のために、非常にシンプルでクリーンなソリューションがあります

ALTER TABLE if exists <tablename> add if not exists <columnname> <columntype>

1

以下の関数は、存在する場合は列をチェックして適切なメッセージを返し、そうでない場合は列をテーブルに追加します。

create or replace function addcol(schemaname varchar, tablename varchar, colname varchar, coltype varchar)
returns varchar 
language 'plpgsql'
as 
$$
declare 
    col_name varchar ;
begin 
      execute 'select column_name from information_schema.columns  where  table_schema = ' ||
      quote_literal(schemaname)||' and table_name='|| quote_literal(tablename) || '   and    column_name= '|| quote_literal(colname)    
      into   col_name ;   

      raise info  ' the val : % ', col_name;
      if(col_name is null ) then 
          col_name := colname;
          execute 'alter table ' ||schemaname|| '.'|| tablename || ' add column '|| colname || '  ' || coltype; 
      else
           col_name := colname ||' Already exist';
      end if;
return col_name;
end;
$$

特に、DOがpostgresへの最近の追加であるので、非常に合理的な答えとして私を驚かせます
John Powell

1

これは基本的にsolaからのソリューションですが、少しクリーンアップされています。それは、私が彼の解決策を単に「改善」したくなかっただけでなく十分に異なっています(それに、私は一種の失礼だと思います)。

主な違いは、EXECUTE形式を使用することです。少しすっきりしていると思いますが、PostgresSQL 9.1以降を使用している必要があります。

これは9.1でテストされ、動作します。注:schema / table_name / data_typeが無効な場合、エラーが発生します。これは「修正」できますが、多くの場合は正しい動作である可能性があります。

CREATE OR REPLACE FUNCTION add_column(schema_name TEXT, table_name TEXT, 
column_name TEXT, data_type TEXT)
RETURNS BOOLEAN
AS
$BODY$
DECLARE
  _tmp text;
BEGIN

  EXECUTE format('SELECT COLUMN_NAME FROM information_schema.columns WHERE 
    table_schema=%L
    AND table_name=%L
    AND column_name=%L', schema_name, table_name, column_name)
  INTO _tmp;

  IF _tmp IS NOT NULL THEN
    RAISE NOTICE 'Column % already exists in %.%', column_name, schema_name, table_name;
    RETURN FALSE;
  END IF;

  EXECUTE format('ALTER TABLE %I.%I ADD COLUMN %I %s;', schema_name, table_name, column_name, data_type);

  RAISE NOTICE 'Column % added to %.%', column_name, schema_name, table_name;

  RETURN TRUE;
END;
$BODY$
LANGUAGE 'plpgsql';

使用法:

select add_column('public', 'foo', 'bar', 'varchar(30)');

0

移行スクリプトに追加して関数を呼び出し、完了したらドロップできます。

create or replace function patch_column() returns void as
$$
begin
    if exists (
        select * from information_schema.columns
            where table_name='my_table'
            and column_name='missing_col'
     )
    then
        raise notice 'missing_col already exists';
    else
        alter table my_table
            add column missing_col varchar;
    end if;
end;
$$ language plpgsql;

select patch_column();

drop function if exists patch_column();

0

私の場合、それが作成された理由により、移行スクリプトが異なるスキーマにまたがることは少し難しいです。

これを回避するために、エラーをキャッチして無視した例外を使用しました。これには、見やすくなるという良い副作用もありました。

ただし、他のソリューションには、おそらくこのソリューションを上回る独自の利点があることに注意してください。

DO $$
BEGIN
  BEGIN
    ALTER TABLE IF EXISTS bobby_tables RENAME COLUMN "dckx" TO "xkcd";
  EXCEPTION
    WHEN undefined_column THEN RAISE NOTICE 'Column was already renamed';
  END;
END $$;

-1

あなたは以下の方法でそれを行うことができます。

ALTER TABLE tableName drop column if exists columnName; 
ALTER TABLE tableName ADD COLUMN columnName character varying(8);

そのため、列がすでに存在する場合は削除されます。次に、特定のテーブルに列を追加します。


17
データを失うことはどうですか?
Aliaksei Ramanau

48
あなたはいつもあなたのクライアントに謝罪するかもしれません
konzo

コラムを追加したばかりなので、とても便利でした。
ヌメノン2018年

-4

クエリがcolumn_nameを返したかどうかを確認するだけです。

そうでない場合は、次のように実行します。

ALTER TABLE x ADD COLUMN y int;

'x'と 'y'に役立つものを配置した場所と、もちろんintを使用した適切なデータ型。


どんな環境にいますか?提案にスクリプト言語はありますか?それともPL / pgSQLを使用していますか?PHP / Java / etcのような言語から実行していますか?
Erwin Moller 2012

スクリプト言語はありません。これはSQL内でのみ行う必要があります。入力時にSQLスクリプトを取得し、選択したデータベースでそのスクリプトを実行するJavaアプリケーションがあります。
marioosh 2012

2
それから私はにPL / pgSQLを検討することをアドバイス:postgresql.org/docs/9.1/static/plpgsql.html引数としてCOLUMN_NAMEとTABLE_NAMEをとる関数を作成します。
Erwin Moller 2012
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.