ChatGPT解决这个技术问题 Extra ChatGPT

How can I add a column that doesn't allow nulls in a Postgresql database?

I'm adding a new, "NOT NULL" column to my Postgresql database using the following query (sanitized for the Internet):

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

Each time I run this query, I receive the following error message:

ERROR: column "mycolumn" contains null values

I'm stumped. Where am I going wrong?

NOTE: I'm using pgAdmin III (1.8.4) primarily, but I received the same error when I ran the SQL from within Terminal.


L
Luc M

You have to set a default value.

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;

@SeanBright , you can access to postgres doc offline by doing man ALTER_TABLE :)
To clarify: the default value is only needed to update existing rows, which is why it can be dropped immediately afterwards. All existing rows have been updated when the table was altered (which may take some time, obviously)
This won't work if you want to use another column to compute the initial value for existing rows. j_random_hacker's answer allows for that, making it more robust.
j
j_random_hacker

As others have observed, you must either create a nullable column or provide a DEFAULT value. If that isn't flexible enough (e.g. if you need the new value to be computed for each row individually somehow), you can use the fact that in PostgreSQL, all DDL commands can be executed inside a transaction:

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;

even in a transaction, NOT NULL is enforced immediately, so must first add column, fill values, then add NOT NULL — as this answer does. (tested on postgres 9.6)
S
Sean Bright

Since rows already exist in the table, the ALTER statement is trying to insert NULL into the newly created column for all of the existing rows. You would have to add the column as allowing NULL, then fill the column with the values you want, and then set it to NOT NULL afterwards.


Example of how to do it would have been really nice. Otherwise, Luc's solution seems to be more complete and ready to use.
P
Paul Tomblin

You either need to define a default, or do what Sean says and add it without the null constraint until you've filled it in on the existing rows.


j
jacktrade

this query will auto-update the nulls

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

R
Ryan Graham

Specifying a default value would also work, assuming a default value is appropriate.


It would improve the answer to give the amended syntax to create the column with a default value (for illustration).
a
alphadogg

Or, create a new table as temp with the extra column, copy the data to this new table while manipulating it as necessary to fill the non-nullable new column, and then swap the table via a two-step name change.

Yes, it is more complicated, but you may need to do it this way if you don't want a big UPDATE on a live table.


I didn't -1 you, but I think there may be subtle difficulties with this -- e.g. I'm betting that existing indices, triggers and views will continue to refer to the original table even after the rename as I think they store the relid of the table (which doesn't change) rather than its name.
Yes, I should have stated that the new table should be an exact copy of the original, including adding indices and such. My bad for being too brief. The reason for this is that there are also subtle diffculties of performing an ALTER on a table that may be live, and sometimes you need to stage it.
For example, using the DEFAULT approach, you'll add that default value to each row. Not sure how Postgres locks up a table when doing this. Or, if column order matters, you can't just add a column with the ALTER command.
Fair enough, ALTER TABLE does lock the table according to the PostgreSQL docs, however your non-transactional approach risks losing changes if the table is indeed live. Also I'd suggest that any code that relies on column order is broken (that may be outside your control of course).
This approach is also particularly problematic if the table is referenced by foreign key indexes as those would all have to be recreated as well.
t
timxor

This worked for me: :)

ALTER TABLE your_table_name ADD COLUMN new_column_name int;

There is no NOT NULL constraint on your query. Of course it's working.