web-dev-qa-db-ja.com

PostgreSQLに存在しない列を追加する方法は?

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

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

「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インジェクション攻撃にさらされるため、災害のレシピになります。

118
Matthew Wood

Postgres 9.6 を使用すると、オプションif not existsを使用してこれを実行できます。

ALTER TABLE table_name ADD COLUMN IF NOT EXISTS column_name INTEGER;
262
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に使用します。a)SQLインジェクションを防ぎ、b)両方の妥当性をすぐにチェックします(最も安価な方法)。列名_colは、quote_ident()EXECUTEに対してサニタイズする必要があります。この関連する回答の詳細:

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

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

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

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

ヒント:EXCEPTION句を含むブロックは、ブロックを持たないブロックよりも出入りが非常に高価です。したがって、必要なくEXCEPTIONを使用しないでください。

21

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

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
$$
17
Vivek S.

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

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;
$$
1
solaimuruganv

これは基本的に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)');
1
David S

次の方法で実行できます。

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

そのため、列が既に存在する場合はドロップします。次に、特定のテーブルに列を追加します。

0
parthivrshah

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

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
user645527

私の場合、それがどのように作成されたのかという理由から、移行スクリプトが異なるスキーマを横断するのは少し難しいです。

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

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

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 $$;
0
ThinkBonobo