在Postgres中向现有列添加“serial”。
问题的出现原因:
在Postgres中,如果想要向现有的列中添加序列(serial),需要使用CREATE SEQUENCE语句和ALTER TABLE语句。然而,CREATE SEQUENCE语句的START [WITH]子句只接受一个值,而不接受子查询。因此,不能直接使用SELECT max(a) + 1 FROM foo的结果作为START [WITH]的值。
解决方法:
有两种解决方法可以实现向现有列中添加序列。
1. 非交互式解决方案:
使用setval()函数可以解决这个问题。可以使用SELECT setval('foo_a_seq', max(a)) FROM foo将序列的当前值设置为现有列的最大值。
如果现有列中没有数据,可以使用coalesce()函数设置默认值。例如,可以使用SELECT setval('foo_a_seq', coalesce(max(a), 0)) FROM foo设置序列的当前值为现有列的最大值,如果现有列中没有数据,则设置默认值为0。然而,将序列的当前值设置为0是不恰当的,因此更合适的做法是使用三参数形式的setval()函数,即SELECT setval('foo_a_seq', coalesce(max(a), 0) + 1, false) FROM foo。将setval()函数的第三个参数设置为false可以防止下一个nextval()在返回值之前提前增加序列的当前值。
另外,可以直接在CREATE SEQUENCE语句中指定拥有序列的列,而不必在之后进行ALTER TABLE操作。
向现有列中添加序列的完整解决方案如下:
CREATE SEQUENCE foo_a_seq OWNED BY foo.a;
SELECT setval('foo_a_seq', coalesce(max(a), 0) + 1, false) FROM foo;
ALTER TABLE foo ALTER COLUMN a SET DEFAULT nextval('foo_a_seq');
2. 使用函数:
如果需要为多个列添加序列,可以使用函数来实现。下面是一个使用函数的示例代码:
CREATE OR REPLACE FUNCTION make_into_serial(table_name TEXT, column_name TEXT) RETURNS INTEGER AS $$
DECLARE
start_with INTEGER;
sequence_name TEXT;
BEGIN
sequence_name := table_name || '_' || column_name || '_seq';
EXECUTE 'SELECT coalesce(max(' || column_name || '), 0) + 1 FROM ' || table_name
INTO start_with;
EXECUTE 'CREATE SEQUENCE ' || sequence_name ||
' START WITH ' || start_with ||
' OWNED BY ' || table_name || '.' || column_name;
EXECUTE 'ALTER TABLE ' || table_name || ' ALTER COLUMN ' || column_name ||
' SET DEFAULT nextVal(''' || sequence_name || ''')';
RETURN start_with;
END;
$$ LANGUAGE plpgsql VOLATILE;
使用这个函数的方法如下:
INSERT INTO foo (data) VALUES ('asdf');
-- ERROR: null value in column "a" violates not-null constraint
SELECT make_into_serial('foo', 'a');
INSERT INTO foo (data) VALUES ('asdf');
-- OK: 1 row(s) affected
以上就是向现有列中添加序列的原因和解决方法。
在上述命令中,创建了两个表foo和bar,其中bar表中的列a使用了serial类型。接下来的代码块是将foo表转换为bar表的命令。其中包括创建序列foo_a_seq并将其作为默认值和非空约束添加到foo表的列a上,以及将序列foo_a_seq与foo表的列a关联起来。然后通过查询foo表中列a的最大值来设置序列foo_a_seq的当前值,并插入一条记录到foo和bar表中。最后,通过查询两个表的内容来验证转换是否成功。
问题的原因是要将一个已存在的表foo转换为另一个表bar,其中bar表的列a使用了serial类型,而foo表的列a并没有使用serial类型。解决方法是通过创建序列并将其与foo表的列a关联起来,然后设置序列的当前值为foo表中列a的最大值。
以下是整理后的
在Postgres中将已存在的列添加为'serial'
当需要将一个已存在的表转换为另一个表,其中目标表的某一列使用了'serial'类型,而原表的对应列没有使用'serial'类型时,可以通过以下步骤来实现。
首先,创建一个序列并将其作为默认值和非空约束添加到原表的列上。具体的操作如下:
CREATE SEQUENCE foo_a_seq; ALTER TABLE foo ALTER COLUMN a SET DEFAULT nextval('foo_a_seq'); ALTER TABLE foo ALTER COLUMN a SET NOT NULL; ALTER SEQUENCE foo_a_seq OWNED BY foo.a;
上述代码中,创建了一个名为foo_a_seq的序列,并将其作为列a的默认值和非空约束添加到表foo中。同时,通过ALTER SEQUENCE命令将序列foo_a_seq与表foo的列a关联起来。
接下来,通过查询原表中列a的最大值来设置序列foo_a_seq的当前值。具体的操作如下:
SELECT MAX(a) FROM foo; SELECT setval('foo_a_seq', 5); -- 将5替换为SELECT MAX的结果
上述代码中,通过查询foo表中列a的最大值来获取当前序列的值,并使用setval函数将序列foo_a_seq的当前值设置为查询结果。
最后,插入一条记录到原表和目标表中,以验证转换是否成功。具体的操作如下:
INSERT INTO foo (b) VALUES('teste'); INSERT INTO bar (b) VALUES('teste');
上述代码中,分别向原表foo和目标表bar中插入一条记录。
最后,通过查询原表和目标表的内容来验证转换是否成功。具体的操作如下:
SELECT * FROM foo; SELECT * FROM bar;
上述代码中,分别查询原表foo和目标表bar的内容。
需要注意的是,如果目标表是由不同的用户创建的,需要先执行ALTER TABLE foo OWNER TO current_user;
将表foo的所有权转移到当前用户。
如果需要设置序列的当前值为MAX(a)+1
,可以使用以下代码:
SELECT MAX(a)+1 FROM foo; SELECT setval('foo_a_seq', 6);
上述代码中,通过查询foo表中列a的最大值加1来设置序列foo_a_seq的当前值,并使用setval函数将序列的当前值设置为查询结果。
通过以上步骤,我们可以将一个已存在的表转换为另一个表,并将目标表的某一列设置为'serial'类型,实现相应的功能。