便携式SQL:唯一主键

时间:2013-05-23 14:25:44

标签: sql sql-server oracle postgresql

尝试开发一些可以在更大的RDBMS之间移植的东西。

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

这里有两个主题

  1. 用于生成自动增量数字的机制。
  2. 如何指定要将其用作主键 表
  3. 我正在寻找对认为当前状况的验证:

    不幸的是,标准化来到这个领域的时间较晚,在某些方面仍未实施(作为强制性标准)。这意味着2013年仍然无法以可移植的方式编写CREATE TABLE语句...如果您希望它使用自动生成的主键。

    真的可以吗?

    Re(1)。这是标准化的,因为它出现在SQL:2003中。据我所知,走路是SEQUENCEs。我相信这些是SQL:2003的强制性部分,对吧?另一种可能性是IDENTITY关键字,它也在SQL:2003中定义,但据我所知,这是标准的可选部分......这意味着像Oracle这样的关键播放器没有实现它。并且仍然可以声称合规。好的,所以SEQUENCEs是指定的便携式方法,对吗?

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

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

    这是对的吗?

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

1 个答案:

答案 0 :(得分:2)

至于"可移植创建表语句":它从数据类型开始:无论boolean,int还是long数据类型是否是任何SQL标准的一部分,我真的很感激这些类型。 PostgreSql支持这些数据类型,Oracle则不支持。具有讽刺意味的是,Oracle在PL / SQL中支持布尔值,但不支持表中的数据类型。表格/列名称等的长度在Oracle中限制为30个字符。所以即便是最简单的创造表#34;永远是便携式的。

至于自动生成的主键:我不知道可移植的语法,所以我没有在" create table"中定义它。当然,这只会延迟问题,并将其留给插入语句。本主题与另一个问题相关:在以最有效的方式使用JDBC进行插入后获取生成的密钥。这在Oracle和PostgreSql之间存在很大差异,如果您曾经在Oracle中使用区分大小写的表/列名称,那么它就不会有趣。

对于约束,我更喜欢在" create table"之后将它们添加到单独的语句中。如果在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功能的完全访问。我们编写了自己的库,后来我们发现关键思想与#34; Anorm"。

相匹配。

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

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

如果你真的希望它只在数据库中解决,那么有一种可能性:在存储过程中放入任何内容,在Java / Scala中编写这些内容,并限制自己在应用程序中调用这些程序,并阅读结果集。这当然只是将复杂性从应用程序层移到数据库中,但是你接受了黑客攻击: - )

如果使用Java存储过程,触发器是非常标准化的。如果您的数据库,您的数据中心人员和您的同事都支持您的数据库。还应考虑非技术/社会方面。我甚至听说过数据库调优人员不接受一般的"左外连接"句法;他们坚持使用Oracle的方式使用"(+)"。

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

<强>更新

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

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

Oracle要求您在创建预准备语句时指定要显式获取其值的(主键)列。这有效,但前提是您不使用区分大小写的表名。在这种情况下,您收到的只是一个误导性的 ORA-00942:表或视图不存在抛出在Oracle的JDBC驱动程序中:Oracle的JDBC驱动程序中存在/是一个错误,我还没有找到使用可移植JDBC方法获取值的方法。因此,需要额外的专有&#34;选择sequence.currVal来自双重&#34;在插入后的同一事务中,您可以取回主键。在我们的情况下,额外的时间是可以接受的,我们比较了插入100000行的时间:在Oracle表现更好之后,PostgreSql要快到第10000行。

a stackoverflow question regarding the ways to get the primary keythe bug report with case sensitive table names from 2008

这个例子很好地展示了这些问题。通常PostgreSql遵循您期望它的工作方式,但您可能必须为Oracle找到一种特殊的方式。