26

考虑下表,其中大约有 10M 行

CREATE TABLE user
(
  id bigint NOT NULL,
  ...
  CONSTRAINT user_pk PRIMARY KEY (id)
)
WITH (
  OIDS=FALSE
)

然后我应用了以下更改

ALTER TABLE USER ADD COLUMN BUSINESS_ID    VARCHAR2(50);
--OK
UPDATE USER SET BUSINESS_ID = ID; //~1500 sec
--OK
ALTER TABLE USER ALTER COLUMN BUSINESS_ID SET NOT NULL;

    ERROR: column "business_id" contains null values
    SQL state: 23502

这很奇怪,因为 id 列(已复制到 business_id 列)不能包含空值,因为它是主键,但要确保我检查它

select count(*) from USER where BUSINESS_ID is null
    --0 records

我怀疑这是一个错误,只是想知道我是否遗漏了一些琐碎的事情

4

3 回答 3

23

唯一合乎逻辑的解释是并发INSERT.
(使用tbl而不是保留字user作为表名。)

ALTER TABLE tbl ADD COLUMN BUSINESS_ID    VARCHAR2(50);
--OK
UPDATE tbl SET BUSINESS_ID = ID; //~1500 sec
--OK

-- concurrent INSERT HERE !!!

ALTER TABLE tbl ALTER COLUMN BUSINESS_ID SET NOT NULL;</code></pre>

为了防止这种情况,请改用:

ALTER TABLE tbl
  ADD COLUMN BUSINESS_ID VARCHAR(50) DEFAULT '';  -- or whatever is appropriate
...

您最终可能会在某些行中获得默认值。你可能想检查一下。

或将所有内容作为事务块运行:

BEGIN;
-- LOCK tbl; -- not needed
ALTER ...
UPDATE ...
ALTER ...
COMMIT;

您可能会使用排他锁来确定,但无论如何ALTER TABLE .. ADD COLUMN都会使用锁。ACCESS EXCLUSIVE(它只在事务结束时释放,就像所有的锁一样。)

于 2013-10-23T16:58:31.903 回答
11

也许它想要一个默认值?ALTER 上的 Postgresql 文档

要添加列,请使用如下命令:

ALTER TABLE products ADD COLUMN description text;

新列最初填充了给定的任何默认值(如果您未指定 DEFAULT 子句,则为 null)。

所以,

ALTER TABLE USER ALTER COLUMN BUSINESS_ID SET DEFAULT="", 
                 ALTER COLUMN BUSINESS_ID SET NOT NULL;
于 2017-02-10T03:11:32.857 回答
0

您不能在同一笔交易中这样做。添加您的列并更新它。然后在单独的事务中设置非空约束。

于 2014-12-11T18:33:46.920 回答