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

9
로 닫는 END; $$것은 구문 오류입니다 (Postgres 9.3). END $$;대신 대신 사용해야 했습니다
LightSystem

5
좋은 접근 방법이지만 왜 중첩 된 BEGIN / END가 차단됩니까? 그것은 나를 위해 단일 레이어로 잘 작동합니다. 또한 마지막에 세미콜론 ($$;)을 추가하면 명령문이 psql에 대해 모호하지 않게됩니다.
Shane

1
이 접근 방식 EXCEPTION은 좀 더 일반적이며 IF NOT EXISTS구문 이없는 작업에 사용할 수 있습니다 ALTER TABLE ... ADD CONSTRAINT.
Tomasz Gandor

389

포스트 그레스 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
스택 오버플로는 실제로 허용 된 답변 변경을 지원해야합니다.
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명령문은 아무것도 리턴 할 수 없습니다. 그리고 반복적으로 사용한다면 함수를 만들 것입니다.

  • I는 사용 객체 식별자 유형 regclassregtype_tbl하고 _type있는 a) 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필요없이 사용 하지 마십시오 .


나는 당신의 솔루션을 내 것보다 더 좋아합니다! 더 안전하고 빠릅니다.
David S

내가 작업 해야하는 Postgres 버전에는 DO진술 이 없으며 약간 수정하여 DEFAULT완벽하게 작동했습니다!
Renab

18

다음의 select 쿼리는 함수를 true/false사용하여 를 반환 EXISTS()합니다.

EXISTS () : EXISTS
의 인수는 임의의 SELECT 문 또는 하위 쿼리입니다. 부속 조회는 행을 리턴하는지 판별하기 위해 평가됩니다. 하나 이상의 행을 반환하면 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
중복 테이블 이름과 열 이름은 여러 스키마에 존재할 수 있습니다.
Mike Sherrill 'Cat Recall'8

1
스키마를 설명하기 위해 코드를 다시 작성하는 것이 좋습니다.
Mike Sherrill 'Cat Recall'12

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

이것은 기본적으로 솔라의 해결책이지만 조금만 정리했습니다. 내가 그의 해결책을 "개선"하고 싶지 않다는 것은 충분히 다르다.

주요 차이점은 EXECUTE 형식을 사용한다는 것입니다. 좀 더 깨끗하다고 ​​생각하지만 PostgresSQL 9.1 이상을 사용해야한다는 것을 의미합니다.

이것은 9.1에서 테스트되었으며 작동합니다. 참고 : schema / table_name / or 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

방금 열을 추가 했으므로 매우 편리했습니다.
Noumenon

-4

쿼리가 column_name을 반환했는지 확인하십시오.

그렇지 않은 경우 다음과 같이 실행하십시오.

ALTER TABLE x ADD COLUMN y int;

'x'와 'y'에 유용한 것을 넣은 곳과 물론 int를 사용한 적절한 데이터 유형.


어떤 환경에 있습니까? 당신의 제안에 스크립팅 언어가 있습니까? 아니면 PL / pgSQL을 사용하고 있습니까? PHP / Java / etc와 같은 언어로 실행하고 있습니까?
Erwin Moller

스크립팅 언어가 없습니다. 이 작업 은 SQL 내에서만 수행해야합니다 . 입력시 SQL 스크립트를 가져 와서 선택한 db에서 해당 스크립트를 실행하는 Java 응용 프로그램이 있습니다.
marioosh

2
그런 다음 pl / pgsql을 살펴 보는 것이 좋습니다. postgresql.org/docs/9.1/static/plpgsql.html column_name 및 table_name을 인수로 사용하는 함수를 만듭니다.
Erwin Moller
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.