Postgres 序列的 last_value 字段未按预期工作
Postgres sequence's last_value field does not work as expected
我在 postgres 中有一个 table,它的主键是使用序列指定的(我们称它为 'a_seq')。该序列用于递增值并将当前值作为正在插入的记录的主键插入。
我用于序列的代码:
CREATE SEQUENCE public.a_seq
INCREMENT 1
START 1
MINVALUE 1
MAXVALUE 9223372036854775807
CACHE 1;
ALTER SEQUENCE public.AssembleTable_RowId_seq OWNER TO postgres;
我正在尝试从磁盘复制文件并将有关复制文件的信息插入到 table。磁盘中有同名文件,因此我使用以下查询从序列中检索 "last_value":
SELECT last_value FROM a_seq;
并重命名文件“_”,然后将其插入数据库,这样文件名和该文件的主键 (id) 是连贯的,例如:
id | fileName
1 | 1_asd.txt
但是当我插入记录时,id 总是比从查询中获取的 "last_value" 大 1 个值,所以 table 看起来像这样:
id | fileName
2 | 1_asd.txt
而且我已经尝试多次执行上面的 select 查询以检查它是否增加了值但它没有。
知道如何在插入之前获取将分配给记录的值吗?
注意:我使用 MATLAB,这是用于插入的代码:
colnames = {'DataType' , ...
'FilePath' , ...
'FileName' , ...
'FileVersion' , ...
'CRC32Q' , ...
'InsertionDateTime', ...
'DataSource' };
data = {FileLine{5} ,... % DataType
tempPath ,... % FilePath
FileLine{1} ,... % FileName
FileLine{2} ,... % FileVersion
FileLine{3} ,... % CRC32Q
FileLine{4} ,... % InsertionDateTime
FileLine{6} ,... % DataSource};
data_table = cell2table(data, 'VariableNames', colnames);
datainsert(conn , 'CopiedFiles' , colnames , data_table);
已更新
我相信你会发生的事情是:当你 select last_value
- 你得到 上次使用的序列值 当你 insert
行时,默认值因为 id
是 nextval
,它将值滚动到比...
上一个
我相信你在中间步骤的某个地方有一个额外的 nextval
。如果您在一个语句中执行它,它会按您预期的那样工作,例如:
t=# create table so12(s int default nextval('s'), t text);
CREATE TABLE
t=# insert into so12(t) select last_value||'_abc.txt' from s;
INSERT 0 1
t=# select * from so12;
s | t
---+-----------
1 | 1_abc.txt
(1 row)
更新2
正如 Nick Barnes 注意到的那样,进一步(然后是 initial1)迭代会给出错误的结果,所以你需要使用他提出的 CASE
logic
当执行 INSERT
语句时没有 id
的值 - Postgres 使用 next_val
自动从序列中获取它。变量 colnames
中的列列表没有 id
,因此 PG 从序列中获取下一个值。要解决此问题,您可以将 id 添加到 colnames
.
这是 Postgres 实现序列的方式中的一个怪癖;作为事务数据库中固有的 non-transactional 个对象,它们的行为有点奇怪。
您第一次在序列上调用 nextvalue()
时,它 不会 影响您在 a_seq.last_value
中看到的数字。但是,它 将 翻转 a_seq.is_called
标志:
test=# create sequence a_seq;
test=# select last_value, is_called from a_seq;
last_value | is_called
------------+-----------
1 | f
test=# select nextval('a_seq');
nextval
---------
1
test=# select last_value, is_called from a_seq;
last_value | is_called
------------+-----------
1 | t
所以如果你需要序列中的下一个值,你会想要像
这样的东西
SELECT
last_value + CASE WHEN is_called THEN 1 ELSE 0 END
FROM a_seq
请注意,如果两个进程同时执行此操作,这将严重中断,因为无法保证您会从下一个 nextval()
调用中实际收到此值。在那种情况下,如果你真的需要 filename
来匹配 id
,你需要用 trigger 生成它,或者一旦你知道它是什么 UPDATE
id
是。
但根据我的经验,最好避免数据和密钥之间存在任何依赖关系。如果你只需要一个独特的filename
,我会创建一个独立的filename_seq
。
为避免您的数据与密钥之间存在任何依赖关系,请尝试:
CREATE SEQUENCE your_sequence
INCREMENT 1
MINVALUE 1
MAXVALUE 9223372036854775807
START 1
CACHE 1;
ALTER TABLE your_sequence
OWNER TO postgres;
我在 postgres 中有一个 table,它的主键是使用序列指定的(我们称它为 'a_seq')。该序列用于递增值并将当前值作为正在插入的记录的主键插入。
我用于序列的代码:
CREATE SEQUENCE public.a_seq
INCREMENT 1
START 1
MINVALUE 1
MAXVALUE 9223372036854775807
CACHE 1;
ALTER SEQUENCE public.AssembleTable_RowId_seq OWNER TO postgres;
我正在尝试从磁盘复制文件并将有关复制文件的信息插入到 table。磁盘中有同名文件,因此我使用以下查询从序列中检索 "last_value":
SELECT last_value FROM a_seq;
并重命名文件“_”,然后将其插入数据库,这样文件名和该文件的主键 (id) 是连贯的,例如:
id | fileName
1 | 1_asd.txt
但是当我插入记录时,id 总是比从查询中获取的 "last_value" 大 1 个值,所以 table 看起来像这样:
id | fileName
2 | 1_asd.txt
而且我已经尝试多次执行上面的 select 查询以检查它是否增加了值但它没有。
知道如何在插入之前获取将分配给记录的值吗?
注意:我使用 MATLAB,这是用于插入的代码:
colnames = {'DataType' , ...
'FilePath' , ...
'FileName' , ...
'FileVersion' , ...
'CRC32Q' , ...
'InsertionDateTime', ...
'DataSource' };
data = {FileLine{5} ,... % DataType
tempPath ,... % FilePath
FileLine{1} ,... % FileName
FileLine{2} ,... % FileVersion
FileLine{3} ,... % CRC32Q
FileLine{4} ,... % InsertionDateTime
FileLine{6} ,... % DataSource};
data_table = cell2table(data, 'VariableNames', colnames);
datainsert(conn , 'CopiedFiles' , colnames , data_table);
已更新
我相信你会发生的事情是:当你 select last_value
- 你得到 上次使用的序列值 当你 insert
行时,默认值因为 id
是 nextval
,它将值滚动到比...
上一个
我相信你在中间步骤的某个地方有一个额外的 nextval
。如果您在一个语句中执行它,它会按您预期的那样工作,例如:
t=# create table so12(s int default nextval('s'), t text);
CREATE TABLE
t=# insert into so12(t) select last_value||'_abc.txt' from s;
INSERT 0 1
t=# select * from so12;
s | t
---+-----------
1 | 1_abc.txt
(1 row)
更新2
正如 Nick Barnes 注意到的那样,进一步(然后是 initial1)迭代会给出错误的结果,所以你需要使用他提出的 CASE
logic
当执行 INSERT
语句时没有 id
的值 - Postgres 使用 next_val
自动从序列中获取它。变量 colnames
中的列列表没有 id
,因此 PG 从序列中获取下一个值。要解决此问题,您可以将 id 添加到 colnames
.
这是 Postgres 实现序列的方式中的一个怪癖;作为事务数据库中固有的 non-transactional 个对象,它们的行为有点奇怪。
您第一次在序列上调用 nextvalue()
时,它 不会 影响您在 a_seq.last_value
中看到的数字。但是,它 将 翻转 a_seq.is_called
标志:
test=# create sequence a_seq;
test=# select last_value, is_called from a_seq;
last_value | is_called
------------+-----------
1 | f
test=# select nextval('a_seq');
nextval
---------
1
test=# select last_value, is_called from a_seq;
last_value | is_called
------------+-----------
1 | t
所以如果你需要序列中的下一个值,你会想要像
这样的东西SELECT
last_value + CASE WHEN is_called THEN 1 ELSE 0 END
FROM a_seq
请注意,如果两个进程同时执行此操作,这将严重中断,因为无法保证您会从下一个 nextval()
调用中实际收到此值。在那种情况下,如果你真的需要 filename
来匹配 id
,你需要用 trigger 生成它,或者一旦你知道它是什么 UPDATE
id
是。
但根据我的经验,最好避免数据和密钥之间存在任何依赖关系。如果你只需要一个独特的filename
,我会创建一个独立的filename_seq
。
为避免您的数据与密钥之间存在任何依赖关系,请尝试:
CREATE SEQUENCE your_sequence
INCREMENT 1
MINVALUE 1
MAXVALUE 9223372036854775807
START 1
CACHE 1;
ALTER TABLE your_sequence
OWNER TO postgres;