手动插入具有主键序列的 postgres 表
我一生中第一次将 MySQL 表转换为 PostgreSQL,并遇到了没有 auto_increment 的传统新手问题。
现在我发现postgres的解决方案是使用一个序列,然后在每次插入时请求这个序列的nextval()作为默认值。我还了解到 SERIAL 类型会自动创建序列和主键,并且即使在事务内部调用 nextval() 也会递增计数器以避免锁定序列。
我找不到解决的问题是,当您手动将值插入具有 UNIQUE 或 PRIMARY 约束的字段以及默认序列的 nextval() 时会发生什么情况。据我所知,当序列达到该值时,这会导致 INSERT 失败。
有没有一种简单(或常见)的方法来解决这个问题?
非常清楚的解释。
更新:如果您认为我不应该这样做、永远无法解决此问题或做出了一些有缺陷的假设,请随时在您的答案中指出。最重要的是,请告诉我该怎么做才能为程序员提供一个稳定而强大的数据库,该数据库不会因简单的插入而被损坏(最好不要将所有内容隐藏在存储过程后面)
I'm converting a MySQL table to PostgreSQL for the first time in my life and running into the traditional newbie problem of having no auto_increment.
Now I've found out that the postgres solution is to use a sequence and then request the nextval() of this sequence as the default value every time you insert. I've also read that the SERIAL type creates a sequence and a primary key automatically, and that nextval() increments the counter even when called inside transactions to avoid locking the sequence.
What I can't find addressed is the issue of what happens when you manually insert values into a field with a UNIQUE or PRIMARY constraint and a nextval() of a sequence as default. As far as I can see, this causes the INSERT to fail when the sequence reaches that value.
Is there a simple (or common) way to fix this ?
A clear explanation would be very much appreciated.
Update: If you feel I shouldn't do this, will never be able to fix this or am making some flawed assumptions, please feel free to point them out in your answers. Above all, please tell me what to do instead to offer programmers a stable and robust database that can't be corrupted with a simple insert (preferably without hiding everything behind stored procedures)
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
如果您要迁移数据,那么我将删除列上的序列约束,执行所有插入,使用 setval() 将序列设置为数据的最大值,然后恢复列序列 nextval() 默认值。
If you're migrating your data then I would drop the sequence constraint on the column, perform all of your inserts, use setval() to set the sequence to the maximum value of your data and then reinstate your column sequence nextval() default.
您可以创建一个触发器来检查
currval('id_sequence_name')>=NEW.id
是否存在。如果您的交易未使用默认值或
nextval('id_sequence_name')
,则currval
函数将引发错误,因为它仅在当前会话中更新序列时才起作用。如果您使用nextval
然后尝试插入更大的主键,那么它会抛出另一个错误。然后事务将被中止。这将防止插入任何会破坏串行的错误主键。
示例代码:
然后使用默认主键插入将工作正常:
但是插入太大的主键将出错并中止:
You can create a trigger which will check if
currval('id_sequence_name')>=NEW.id
.If your transaction did not use default value or
nextval('id_sequence_name')
, then acurrval
function will throw an error, as it works only when sequence was updated in current session. If you usenextval
and then try to insert bigger primary key then it will throw another error. A transaction will be then aborted.This would prevent inserting any bad primary keys which would break serial.
Example code:
Then inserting with default primary key will work fine:
But inserting too big primary key will error out and abort:
为了扩展 Tometzky 的精彩答案,这里有一个更通用的版本:
您可以将其应用于任何列,例如 test.id,因此:
To expand on Tometzky's great answer, here is a more general version:
Which you can apply to any column, say test.id, thusly:
我不太明白你的问题,但如果你的目标只是进行插入,并有一个有效的字段(例如 id),那么插入没有 id 字段的值,这就是“默认”所代表的意思。它会起作用的。
例如,在表定义中拥有
id serial NOT NULL
和CONSTRAINT table_pkey PRIMARY KEY(id)
将自动设置 id 并自动递增序列table_id_seq
。I don't exactly understand you question, but if your goal is just to do the insert, and have a valid field (e.g. an id), then insert the values without the id field, that's what "default" stands for. It will work.
E.g. havin a
id serial NOT NULL
and aCONSTRAINT table_pkey PRIMARY KEY(id)
in the table definition will auto-set the id and auto-increment a sequencetable_id_seq
.使用 CHECK 怎么样?
What about using a CHECK?