Как добавить столбец в базу данных Postgresql, который не допускает нулевые значения?

243

Я добавляю новый столбец «NOT NULL» в свою базу данных Postgresql, используя следующий запрос (очищенный для Интернета):

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

Каждый раз, когда я запускаю этот запрос, я получаю следующее сообщение об ошибке:

ERROR:  column "mycolumn" contains null values

Я в тупике. Куда я иду не так?

ПРИМЕЧАНИЕ: я в основном использую pgAdmin III (1.8.4), но я получил ту же ошибку при запуске SQL из терминала.

Huuuze
источник

Ответы:

400

Вы должны установить значение по умолчанию.

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;
Люк М
источник
1
Хорошее решение. По какой-то причине я не смог попасть в онлайн-документацию postgres, чтобы узнать, какой будет синтаксис для этого.
Шон Брайт
4
@SeanBright, вы можете получить доступ к postgres doc в автономном режиме, выполнив man ALTER_TABLE :)
allan.simon
@ allan.simon Я никогда раньше не использовал PostgreSQL, и у меня его нигде не установлено.
Шон Брайт
2
Чтобы уточнить: значение по умолчанию необходимо только для обновления существующих строк, поэтому его можно сразу же удалить. Все существующие строки были обновлены, когда таблица была изменена (что может занять некоторое время, очевидно)
MSalters
2
Это не будет работать, если вы хотите использовать другой столбец для вычисления начального значения для существующих строк. Ответ j_random_hacker позволяет это сделать, делая его более надежным.
jpmc26
82

Как заметили другие, вы должны либо создать столбец, который можно обнулять, либо указать значение DEFAULT. Если это недостаточно гибко (например, если вам нужно как-то вычислить новое значение для каждой строки отдельно), вы можете использовать тот факт, что в PostgreSQL все команды DDL могут выполняться внутри транзакции:

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
источник
7
даже в транзакции NOT NULL применяется немедленно, поэтому сначала необходимо добавить столбец, заполнить значения, а затем добавить NOT NULL - как и в этом ответе. (проверено на postgres 9.6)
Бени Чернявский-Паскин
48

Поскольку строки уже существуют в таблице, ALTERоператор пытается вставить NULLво вновь созданный столбец для всех существующих строк. Вам нужно будет добавить столбец как разрешающий NULL, затем заполнить столбец требуемыми значениями, а затем установить его на NOT NULLпотом.

Шон Брайт
источник
7
Пример того, как это сделать, был бы действительно хорош. В противном случае решение Люка кажется более полным и готовым к использованию.
Виктор Фараздаги
5

Вам нужно либо определить значение по умолчанию, либо сделать то, что говорит Шон, и добавить его без ограничения NULL, пока вы не заполните его в существующих строках.

Пол Томблин
источник
2

Задание значения по умолчанию также будет работать при условии, что значение по умолчанию является подходящим.

Райан Грэм
источник
2
Было бы улучшить ответ, чтобы дать измененный синтаксис для создания столбца со значением по умолчанию (для иллюстрации).
резкое выступление
1

Или создайте новую таблицу как временную с дополнительным столбцом, скопируйте данные в эту новую таблицу, манипулируя ею по мере необходимости, чтобы заполнить необнуляемый новый столбец, а затем поменяйте местами таблицу с помощью двухэтапного изменения имени.

Да, это сложнее, но вам может потребоваться сделать это таким образом, если вы не хотите большого ОБНОВЛЕНИЯ на живом столе.

alphadogg
источник
3
Я не -1 вас, но я думаю, что с этим могут быть тонкие трудности - например, я держу пари, что существующие индексы, триггеры и представления будут продолжать ссылаться на исходную таблицу даже после переименования, так как я думаю, что они хранят Relid таблицы (которая не меняется), а не ее имя.
j_random_hacker
1
Да, я должен был заявить, что новая таблица должна быть точной копией оригинала, включая добавление индексов и тому подобное. Мой плохой за то, что слишком короток. Причина этого заключается в том, что существуют также тонкие трудности выполнения ALTER на столе, который может быть живым, и иногда вам нужно ставить его.
alphadogg
Например, используя подход DEFAULT, вы добавите это значение по умолчанию к каждой строке. Не уверен, как Postgres блокирует стол при этом. Или, если порядок столбцов имеет значение, вы не можете просто добавить столбец с помощью команды ALTER.
alphadogg
Справедливо, ALTER TABLE блокирует таблицу в соответствии с документацией PostgreSQL, однако ваш не транзакционный подход рискует потерять изменения, если таблица действительно жива. Также я бы предположил, что любой код, который зависит от порядка столбцов, не работает (это может быть вне вашего контроля, конечно).
j_random_hacker
2
Этот подход также особенно проблематичен, если на таблицу ссылаются индексы внешнего ключа, поскольку все они также должны быть созданы заново.
Арье Лейб Таурог
0

этот запрос будет автоматически обновлять нули

ALTER TABLE mytable ADD COLUMN mycolumn character varying(50) DEFAULT 'whatever' NOT NULL;
jacktrade
источник
-6

Это сработало для меня :)

ALTER TABLE your_table_name ADD COLUMN new_column_name int;
timxor
источник
2
На NOT NULLваш запрос нет ограничений. Конечно, это работает.
Сильвен