3

试图开发一些应该在更大的 RDBMS 之间移植的东西。

问题在于生成使用自动递增数字作为表的主键。

这里有两个主题

  1. 用于生成自增数字的机制。
  2. 如何指定要将其用作表的主键。

我正在寻找对我认为是当前事态的验证:

不幸的是,该领域的标准化来得很晚,并且在某些方面仍未实施(作为强制性标准)。这意味着在 2013 年仍然不可能以可移植的方式编写 CREATE TABLE 语句……如果您希望它具有自动生成的主键。

真的可以这样吗?

重新(1)。这是标准化的,因为它出现在 SQL:2003 中。据我了解,要走的路是序列。我相信这些是 SQL:2003 的强制性部分,对吧?另一种可能性是 IDENTITY 关键字,它也在 SQL:2003 中定义,但据我所知,它是标准的可选部分......这意味着像 Oracle 这样的关键参与者没有实现它。 . 并且仍然可以声称合规。好的,所以 SEQUENCEs 是为此指定的可移植方法,对吗?

重新(2)。数据库供应商以不同的方式实现这一点。在 PostgreSQL 中,您可以将 CREATE TABLE 语句直接与序列链接,在 Oracle 中,您必须创建触发器以确保 SEQUENCE 与表一起使用。

所以我的结论是,如果没有针对 (2) 的标准化解决方案,那么所有主要参与者现在都支持 SEQUENCE 确实没有多大帮助。我仍然需要为像 CREATE TABLE 语句这样简单的东西编写特定于 db 的代码。

这是正确的吗?

除了标准及其实施之外,如果有人对问题有一个可移植的解决方案,我也会感兴趣,无论从 RDBMS 最佳实践的角度来看它是否是一个 hack。对于这样的解决方案,它必须独立于任何应用程序,即它必须是解决问题的数据库,而不是应用程序层。也许如果TRIGGERs和SEQUENCEs的概念都可以说是标准化的,那么结合它们两者的解决方案将是可移植的?

4

1 回答 1

3

至于“可移植的创建表语句”:它从数据类型开始:无论布尔、整数或长数据类型是否属于任何 SQL 标准,我都非常欣赏这些类型。PostgreSql 支持这些数据类型,Oracle 不支持。具有讽刺意味的是,Oracle 在 PL/SQL 中支持布尔值,但不支持表中的数据类型。甚至表/列名等的长度在 Oracle 中也被限制为 30 个字符。因此,即使是最简单的“创建表”也不总是可移植的。

至于自动生成的主键:我不知道可移植的语法,所以我没有在“创建表”中定义它。当然,这只会延迟问题,并将其留给插入语句。本主题与另一个问题有关:以最有效的方式使用 JDBC 获取插入后生成的密钥。这在 Oracle 和 PostgreSql 之间有很大的不同,如果你曾经敢在 Oracle 中使用区分大小写的表/列名,那就不好笑了。

至于约束,我更喜欢在“创建表”之后的单独语句中添加它们。如果您在 Oracle 中使用 char(1) 和检查约束实现布尔数据类型,则约束集可能会有所不同,而 PostgreSql 直接支持此数据类型。

至于“标准”:一个例子

SQL99 standard: for SELECT DISTINCT, ORDER BY expressions must appear in select list

此消息来自 PostgreSql,Oracle 11g 没有抱怨。14年后,他们会改变吗?

一般来说,您仍然需要编写特定于数据库的代码。

至于您的结论:在我们的场景中,我们使用模型驱动方法实现了一个可移植的数据库应用程序。这个逻辑元数据由应用程序使用,不同的数据库类型有不同的后端。我们不使用任何 ORM,只使用“直接 SQL”,因为这简化了 SQL 语句的调优,并提供了对所有 SQL 功能的完全访问权限。我们编写了自己的库,后来我们发现关键思想与“Anorm”的这些思想相匹配。

好消息是,虽然有很多小烦恼,但它工作得很好,即使是复杂的查询。例如,窗口聚合函数非常可移植(row_number(),partition by)。您必须在 Oracle 上使用 listagg,而在 PostgreSql 上需要 string_agg。递归注释表表达式在 PostgreSql 中需要“with recursive”,Oracle 不喜欢。PostgreSql 在查询中支持“limit”和“offset”,你需要把它包装在Oracle 中。如果您在 Oracle 和 PostgreSql 中都使用 SQL 数组(将数组作为表中的列),它会让您发疯。Oracle 上有物化视图,但 PostgreSql 中不存在。令人惊讶的是,不仅可以用 Java 编写数据库存储过程,还可以用 Scala 编写数据库存储过程,这在 Oracle 和 PostgreSql 中运行得非常好。此列表不完整。但到目前为止,我们设法为任何“可移植性问题”找到了一个可接受的(= 快速)解决方案。

它有回报吗?在我们的场景中,有一个中央 Oracle 安装(RAC,读/写),但在每个应用程序服务器上都有分布式 PostgreSql 安装作为 localhost 数据库(只读)。这大大提高了性能和可扩展性,而不会造成成本损失。

如果你真的想只在数据库中解决它,有一种可能性:将任何东西放在存储过程中,用 Java/Scala 编写它们,并限制自己在应用程序中调用这些过程,并读取结果集。这当然只是将复杂性从应用程序层转移到数据库中,但是您接受了 hack :-)

如果您使用 Java 存储过程,触发器是相当标准化的。如果您的数据库、您的管理层、您的数据中心人员和您的同事都支持它。非技术/社会方面也需要考虑。我什至听说过不接受一般“左外连接”语法的数据库调优人。他们坚持使用“(+)”的Oracle方式。

因此,即使触发器 (PL/SQL) 和序列被标准化,也会有很多其他的事情需要考虑。

更新

至于返回生成的主键我只能从JDBC的角度来判断。

如果您使用Statement.getGeneratedKeys ,PostgreSql 会返回它(我认为这是正常的方式)。

Oracle 要求您在创建准备好的语句时指定要显式返回其值的(主键)列。这有效,但前提是您不使用区分大小写的表名。在这种情况下,您收到的只是一个误导性的ORA-00942:Oracle 的 JDBC 驱动程序中抛出的表或视图不存在:Oracle 的 JDBC 驱动程序中存在/存在错误,我还没有找到使用便携式获取值的方法JDBC 方法。因此,在插入后立即在同一事务中以额外的专有“select sequence.currVal from dual”为代价,您可以取回主键。在我们的案例中,额外的时间是可以接受的,我们比较了插入 100000 行的时间:PostgreSql 在第 10000 行之前更快,之后 Oracle 性能更好。

查看有关从 2008 年获取主键和 区分大小写的表名的错误报告的方法的 stackoverflow 问题

这个例子很好地说明了这些问题。通常 PostgreSql 会按照您期望的方式工作,但您可能必须为 Oracle 找到一种特殊的方式。

于 2013-05-23T16:54:32.713 回答