Jak mogę dodać kolumnę do bazy danych Postgresql, która nie dopuszcza wartości zerowych?

243

Dodaję nową kolumnę „NOT NULL” do mojej bazy danych Postgresql, używając następującego zapytania (odkażonego dla Internetu):

ALTER TABLE mytable ADD COLUMN mycolumn character varying(50) NOT NULL;

Za każdym razem, gdy uruchamiam to zapytanie, pojawia się następujący komunikat o błędzie:

ERROR:  column "mycolumn" contains null values

Jestem zakłopotany. Gdzie się mylę?

UWAGA: Korzystam głównie z pgAdmin III (1.8.4), ale otrzymałem ten sam błąd, kiedy uruchomiłem SQL z poziomu terminala.

Huuuze
źródło

Odpowiedzi:

400

Musisz ustawić wartość domyślną.

ALTER TABLE mytable ADD COLUMN mycolumn character varying(50) NOT NULL DEFAULT 'foo';

... some work (set real values as you want)...

ALTER TABLE mytable ALTER COLUMN mycolumn DROP DEFAULT;
Luc M.
źródło
1
Niezłe rozwiązanie. Z jakiegoś powodu nie mogłem dostać się do internetowych dokumentów postgres, aby zobaczyć, jaka byłaby do tego składnia.
Sean Bright
4
@SeanBright, możesz uzyskać dostęp do postgres doc offline, wykonując man ALTER_TABLE :)
allan.simon
@ allan.simon Nigdy wcześniej nie korzystałem z PostgreSQL i nigdzie go nie mam.
Sean Bright,
2
Aby wyjaśnić: wartość domyślna jest potrzebna tylko do aktualizacji istniejących wierszy, dlatego można ją natychmiast usunąć. Wszystkie istniejące wiersze zostały zaktualizowane, gdy tabela została zmieniona (co może oczywiście zająć trochę czasu)
MSalters
2
To nie zadziała, jeśli chcesz użyć innej kolumny do obliczenia wartości początkowej dla istniejących wierszy. Odpowiedź j_random_hackera pozwala na to, dzięki czemu jest bardziej niezawodna.
jpmc26
82

Jak zauważyli inni, musisz albo utworzyć kolumnę zerowalną, albo podać wartość DOMYŚLNĄ. Jeśli to nie jest wystarczająco elastyczne (np. Jeśli potrzebujesz w jakiś sposób obliczać nową wartość dla każdego wiersza indywidualnie), możesz skorzystać z faktu, że w PostgreSQL wszystkie polecenia DDL można wykonać w ramach transakcji:

BEGIN;
ALTER TABLE mytable ADD COLUMN mycolumn character varying(50);
UPDATE mytable SET mycolumn = timeofday();    -- Just a silly example
ALTER TABLE mytable ALTER COLUMN mycolumn SET NOT NULL;
COMMIT;
j_random_hacker
źródło
7
nawet w przypadku transakcji NOT NULL jest egzekwowane natychmiast, więc najpierw należy dodać kolumnę, wypełnić wartości, a następnie dodać NOT NULL - jak robi to odpowiedź. (testowany na postgresie 9.6)
Beni Cherniavsky-Paskin
48

Ponieważ wiersze już istnieją w tabeli, ALTERinstrukcja próbuje wstawić NULLdo nowo utworzonej kolumny dla wszystkich istniejących wierszy. Trzeba dodać kolumnę jako dozwoloną NULL, a następnie wypełnić kolumnę żądanymi wartościami, a następnie ustawić ją na NOT NULLpóźniej.

Sean Bright
źródło
7
Przykład, jak to zrobić, byłby naprawdę miły. W przeciwnym razie rozwiązanie Luca wydaje się bardziej kompletne i gotowe do użycia.
Victor Farazdagi,
5

Musisz zdefiniować wartość domyślną lub zrobić to, co mówi Sean i dodać ją bez ograniczenia zerowego, dopóki nie wypełnisz jej w istniejących wierszach.

Paul Tomblin
źródło
2

Określenie wartości domyślnej również działałoby, przy założeniu, że wartość domyślna jest odpowiednia.

Ryan Graham
źródło
2
Poprawiłoby to odpowiedź, dając poprawioną składnię, aby utworzyć kolumnę z wartością domyślną (dla ilustracji).
hardmath
1

Lub utwórz nową tabelę jako temp z dodatkową kolumną, skopiuj dane do tej nowej tabeli, manipulując nią w razie potrzeby, aby wypełnić nową kolumnę, która nie ma wartości zerowej, a następnie zamień tabelę poprzez dwuetapową zmianę nazwy.

Tak, jest to bardziej skomplikowane, ale może być konieczne zrobienie tego w ten sposób, jeśli nie chcesz dużej aktualizacji na stole na żywo.

alphadogg
źródło
3
Nie -1 cię, ale myślę, że mogą być z tym subtelne trudności - np. Założę się, że istniejące indeksy, wyzwalacze i widoki będą nadal odnosić się do oryginalnej tabeli, nawet po zmianie nazwy, ponieważ myślę, że przechowują relid tabeli (która się nie zmienia) zamiast jej nazwy.
j_random_hacker
1
Tak, powinienem był powiedzieć, że nowa tabela powinna być dokładną kopią oryginału, łącznie z dodawaniem indeksów i tym podobnych. Mój zły za bycie zbyt krótkim. Powodem tego jest to, że istnieją również subtelne trudności z wykonaniem ZMIANY na stole, który może być na żywo, i czasami trzeba go wystawić.
alphadogg
Na przykład, stosując metodę DOMYŚLNĄ, dodasz tę wartość domyślną do każdego wiersza. Nie jestem pewien, w jaki sposób Postgres blokuje stół. Lub, jeśli kolejność kolumn ma znaczenie, nie można po prostu dodać kolumny za pomocą polecenia ALTER.
alphadogg
W porządku, ALTER TABLE blokuje tabelę zgodnie z dokumentacją PostgreSQL, jednak twoje podejście nietransakcyjne grozi utratą zmian, jeśli tabela rzeczywiście jest aktywna. Sugeruję również, aby każdy kod, który opiera się na kolejności kolumn, był uszkodzony (co oczywiście może być poza twoją kontrolą).
j_random_hacker
2
Takie podejście jest szczególnie problematyczne, jeśli do tabeli odwołują się indeksy kluczy obcych, ponieważ wszystkie one również musiałyby zostać odtworzone.
Aryeh Leib Taurog
0

to zapytanie automatycznie zaktualizuje wartości null

ALTER TABLE mytable ADD COLUMN mycolumn character varying(50) DEFAULT 'whatever' NOT NULL;
jacktrade
źródło
-6

To działało dla mnie: :)

ALTER TABLE your_table_name ADD COLUMN new_column_name int;
timxor
źródło
2
NOT NULLTwoje zapytanie nie ma żadnych ograniczeń. Oczywiście, że działa.
Sylvain,