我需要将DDL从Postgres迁移到DB2,但我需要它与Postgres中的相同。有一个表从序列生成值,但也可以明确给出值。
的Postgres
create sequence hist_id_seq;
create table benchmarksql.history (
hist_id integer not null default nextval('hist_id_seq') primary key,
h_c_id integer,
h_c_d_id integer,
h_c_w_id integer,
h_d_id integer,
h_w_id integer,
h_date timestamp,
h_amount decimal(6,2),
h_data varchar(24)
);
(查看hist_id列中的序列调用以定义主键的值)
业务逻辑通过显式提供ID插入表中,在其他情况下,它会离开数据库以选择数字。
如果我在DB2中将其更改为GENERATED ALWAYS,则会抛出错误,因为有一些提供的值。另一方面,如果我使用GENERATED BY DEFAULT创建表,DB2将在尝试使用相同的值(SQL0803N)插入时抛出错误,因为“内部序列”没有考虑已插入的值,并且它不会使用下一个值重试。
并且,每次插入提供的ID时,我都不想重新启动序列。
这是BenchmarkSQL在尝试将其移植到DB2时的问题:https://sourceforge.net/projects/benchmarksql/(File sqlTableCreates)
如何在DB2中实现与Postgres中相同的数据库逻辑(显然在Oracle中)?
答案 0 :(得分:0)
您在误解下操作:数据库外部的源可以指示其内部键。理想情况/概念上,自动生成的ID永远不需要在数据库外部看到,因为从概念上讲,应该有独特的自然键用于导出或报告。尽管如此,有时应用程序需要管理一些ID,通常是在设置相关实体时(例如,JPA似乎希望以这种方式工作)。
但是,如果添加从不同来源生成的id值,则db无法管理它。怎么可能呢?这样做效率不高 - 一方面,尝试这样做会做以下其中一种
(这通常会出现在人们尝试类似:SELECT MAX(id) + 1
的情况时,这需要锁定整个表格以确保线程安全,可能包括甚至不会触及该列的语句。如果您尝试查找任何"首先未使用的" id - 尝试填补空白 - 这会变得更加复杂和有问题)
两者都不理想,所以最好不要首先解决问题。这通常是通过自动生成id列来完成的,但是(如前所述)有些情况下我们可能需要知道在将行插入表之前id是什么。幸运的是,有一个标准的SQL对象SEQUENCE
。这提供了一种数据库管理,线程安全,快速获取ID的方法。看来在PostgreSQL中你可以使用DEFAULT
子句中的序列作为列,但DB2并不允许它。如果你不想每次都指定一个id(它应该在某些时候自动生成),你需要另一种方式;这是使用BEFORE INSERT
触发器的最佳时机;
CREATE TRIGGER Add_Generated_Id NO CASCADE BEFORE INSERT ON benchmarksql.history
NEW AS Incoming_Entity
FOR EACH ROW
WHEN Incoming_Entity.id IS NULL
SET id = NEXTVAL FOR hist_id_seq
(类似这样的事情 - 没有经过测试。你没有指明项目属于哪个地方)
所以,如果你然后添加一行像:
INSERT INTO benchmarksql.history (hist_id, h_data) VALUES(null, 'a')
或
INSERT INTO benchmarksql.history (h_data) VALUES('a')
将自动生成并附加ID。请注意,添加到表中的 ALL ID必须来自给定的序列(正如@mustaccio指出的那样,即使在PostgreSQL中也是如此),或者列上的任何UNIQUE CONSTRAINT
都将启动抛出重复键错误。因此,只要您的应用程序在表格中插入行之前需要id,您就需要某种形式的
SELECT NEXT VALUE FOR hist_id_seq
FROM sysibm.sysdummy1
......那就是它,差不多。这完全是线程和并发安全的,不会维护/需要长期锁定,也不需要序列化访问表。