PostgreSQL'de yoksa sütun nasıl eklenir?


145

Soru basit. Nasıl sütun eklemek xmasaya yfakat sadece xsütun yok? Burada sadece sütun olup olmadığını kontrol etmek için nasıl çözüm buldum .

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

Yanıtlar:


133

İşte "DO" ifadesini kullanan kısa ve tatlı bir sürüm:

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

Bunları parametre olarak aktaramazsınız, istemci tarafındaki dizede değişken ikame yapmanız gerekir, ancak bu yalnızca sütun zaten varsa bir mesaj yayınlayan bağımsız bir sorgudur. diğer hatalarda (geçersiz veri türü gibi) başarısız olmaya devam eder.

Dış kaynaklardan gelen rastgele dizeler ise bu yöntemlerden HERHANGİ BİRİNİ yapmayı önermiyorum. Hangi yöntemi kullanırsanız kullanın (sorgu olarak yürütülen cleint tarafı veya sunucu tarafı dinamik dizeler), sizi SQL enjeksiyon saldırılarına açtığı için felaket için bir reçete olacaktır.


4
DO $$ BEGIN BEGIN CREATE INDEX type_idx ON table1 USING btree (type); EXCEPTION WHEN duplicate_table THEN RAISE NOTICE 'Index exists.'; END; END;$$;Aynı yaklaşım CREATE INDEX;) Cevabınız için teşekkürler,
marioosh

Neden sadece anonim kod bloğunu başlatmanın DO $$başarısız olduğundan emin değilim . Dev postgres belgelerinde bir örnekte verilen DO $$;bloğa başlayana kadar hangisinin başarısız olduğunu da denedim . DO $$DECLARE r record;
nemesisfixx

9
END; $$END $$;
İle

5
İyi yaklaşım, ama neden iç içe BEGIN / END engelliyor? Benim için tek bir tabaka ile iyi çalışıyor. Ayrıca sonuna noktalı virgül ($$;) eklemek ifadeyi psql için açık hale getirir.
Shane

1
Bu yaklaşım ( EXCEPTION) biraz daha geneldir ve IF NOT EXISTSsözdizimi olmayan görevler için kullanılabilir - örneğin ALTER TABLE ... ADD CONSTRAINT.
Tomasz Gandor

390

İle Postgres 9.6 Bu seçeneği kullanılarak yapılabilirif not exists

ALTER TABLE table_name ADD COLUMN IF NOT EXISTS column_name INTEGER;

4
Tatlı. Ne yazık ki, henüz ADD CONSTRAINT IF NOT EXISTSyok.
Tomasz Gandor

4
Bu cevap neden sayfanın alt kısmında, diğer seçeneklerden çok daha iyi.
Ecksters

Sadece meraktan: bu masada bir erişim kilidine neden olur (ve bu nedenle üretim veritabanlarındaki büyük tablolarda çalıştırıldığında bir bakım penceresi gerektirir)?
Hassan Baig

4
Yığın taşması gerçekten kabul edilen cevabı değiştirmeyi desteklemelidir.
Henrik Sommerland

@HenrikSommerland: O edilir - ama sadece soru soruldu kişi tarafından izin verdi.
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;

Aramak:

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

TRUEBaşarıyı döndürür , aksi takdirde FALSE(sütun zaten var).
Geçersiz tablo veya tür adı için bir istisna oluşturur.

Neden başka bir versiyon?

  • Bu bir deyimle yapılabilir DO, ancak DOdeyimler hiçbir şey döndüremez. Ve tekrarlanan kullanım içinse, bir işlev yaratacağım.

  • Kullandığım nesne tanımlayıcısı tipi regclass ve regtypeiçin _tblve _type: a) SQL enjeksiyon ve b) hemen her iki kontrol geçerliliği (mümkün olan en ucuz şekilde) engeller. Sütun adı _colhala dezenfekte edilmesi için gelmiştir EXECUTEile quote_ident(). Bu cevapta daha fazla açıklama:

  • format()Postgres 9.1+ sürümünü gerektirir. Eski sürümler için el ile birleştirme:

    EXECUTE 'ALTER TABLE ' || _tbl || ' ADD COLUMN ' || quote_ident(_col) || ' ' || _type;
  • Tablo adınızı şema niteleyebilir, ancak zorunda değilsiniz.
    Deve durumunu ve ayrılmış sözcükleri korumak için işlev çağrısındaki tanımlayıcıları iki kez tırnak içine alabilirsiniz (ancak bunların hiçbirini yine de kullanmamalısınız).

  • Ben pg_catalogyerine sorgu information_schema. Detaylı açıklama:

  • Şu anda kabul edilen cevapEXCEPTION gibi bir madde içeren bloklar oldukça yavaştır. Bu genellikle daha basit ve daha hızlıdır. Dökümantasyon:

İpucu: Bir EXCEPTIONcümle içeren bir bloğun girilmesi ve çıkması, bir blok olmayan bir bloktan önemli ölçüde daha pahalıdır. Bu nedenle, EXCEPTIONihtiyaç duymadan kullanmayın .


Çözümünü benimkinden daha çok seviyorum! Daha iyi, daha güvenli ve daha hızlı.
David S

Çalışmak zorunda olduğum Postgres'in sürümü, DOkabul etmek için hafif bir modifikasyona sahip değil DEFAULTve bu mükemmel çalıştı!
renab

18

Aşağıdaki seçme sorgusu işlevi true/falsekullanarak dönecektir EXISTS().

EXISTS () :
EXISTS argümanı isteğe bağlı bir SELECT deyimi veya alt sorgusudur. Alt sorgu, satır döndürüp döndürmediğini belirlemek için değerlendirilir. En az bir satır döndürürse, EXISTS sonucu "true" olur; alt sorgu satır döndürmezse, EXISTS sonucu "false" olur

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

ve tablonuzu değiştirmek için aşağıdaki dinamik SQL deyimini kullanın

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
Birden çok şemada yinelenen tablo adları ve sütun adları bulunabilir.
Mike Sherrill 'Cat Recall'

1
Şemaları hesaba katmak için kodunuzu yeniden yazmak isteyebilirsiniz.
Mike Sherrill 'Cat Recall'

2

Postgre 9.5+ kullananlar için (çoğunuzun yaptığınıza inanıyorum) oldukça basit ve temiz bir çözüm var

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

1

Aşağıdaki fonksiyon varsa sütunu kontrol eder, uygun mesajı döndürürse sütunu tabloya ekler.

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

Özellikle DO postgres için yeni bir ek olarak bana çok makul bir cevap olarak vuruyor
John Powell

1

Bu temelde sola'nın çözümü, ancak sadece biraz temizlendi. Sadece onun çözümünü "geliştirmek" istemediğim kadar farklı (artı, bu kaba olduğunu düşünüyorum).

Temel fark EXECUTE biçimini kullanmasıdır. Hangi biraz daha temiz olduğunu düşünüyorum, ama inanıyorum ki PostgresSQL 9.1 veya daha yeni olması gerekir.

Bu 9.1 üzerinde test edildi ve çalışıyor. Not: Şema / tablo_adı / veya veri_türü geçersizse bir hata oluşturur. Bu "düzeltilebilir", ancak birçok durumda doğru davranış olabilir.

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';

kullanımı:

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

0

Geçiş komut dosyalarına eklenebilir işlevi çağırır ve bittiğinde bırak.

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

Benim durumumda, nasıl yaratıldığı için, göç senaryolarımızın farklı şemalarda kesilmesi biraz zor.

Bu sorunu gidermek için, hatayı yakalayıp görmezden gelen bir istisna kullandık. Bu ayrıca bakılması çok daha kolay olmanın güzel bir yan etkisi oldu.

Bununla birlikte, diğer çözümlerin muhtemelen bu çözümden daha ağır olan kendi avantajlarına sahip olduğuna dikkat edin:

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

Bunu takip ederek yapabilirsiniz.

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

Dolayısıyla, zaten varsa sütunu düşürür. Ve sonra sütunu belirli bir tabloya ekleyin.


17
veri kaybetmeye ne dersiniz?
Aliaksei Ramanau

48
Müşterilerinizden her zaman özür
dileyebilirsiniz

Sütunu yeni ekledim, bu yüzden bu benim için çok uygun oldu.
Noumenon

-4

Sorgunun bir sütun_adı döndürüp döndürmediğini kontrol etmeniz yeterlidir.

Değilse, böyle bir şey yürütün:

ALTER TABLE x ADD COLUMN y int;

'X' ve 'y' için yararlı bir şey koyduğunuz ve tabii ki int kullandığım yerde uygun bir veri türü.


Hangi ortamdasınız? Teklifinizde bir betik dili var mı? Yoksa PL / pgSQL mi kullanıyorsunuz? PHP / Java / etc gibi bir dilde mi çalışıyorsunuz?
Erwin Moller

Komut dosyası dili yok. Bunu sadece SQL içinde yapmam gerekiyor . Ben girdi SQL komut dosyası almak ve seçilen db bu komut dosyasını çalıştırmak Java uygulaması var.
marioosh

2
Ardından, pl / pgsql: postgresql.org/docs/9.1/static/plpgsql.html Sütun_adı ve tablo_adı değişkenlerini alan bir işlev oluşturmanızı öneririm .
Erwin Moller
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.