【问题标题】:Return (self) generated value from insert statement (no id, no returning)从插入语句返回(自我)生成的值(无 id,无返回)
【发布时间】:2013-12-25 23:18:54
【问题描述】:

对不起,如果问题标题有误导性或不够准确,但我没有看到如何用一句话问。

假设我们有一个表,其中 PK 是一个字符串(数字从 '100,000' 到 '999,999',逗号仅供阅读)。 也可以说,PK不是按顺序使用的。

现在我想使用 java.sql 在表中插入一个新行,并向用户显示插入行的 PK。由于默认情况下不生成 PK(例如,没有 PK 的插入值不起作用,在给定的环境中像 generate_keys 这样的东西不可用)我看到了两种不同的方法:

在两个不同的语句中,首先找到一个可能的下一个键,然后尝试插入(并期望另一个事务在两个语句之间的时间内使用相同的键) - 重试直到成功是否有效或任何 sql 技巧这里有事务设置/锁定帮助吗?我如何在 java.sql 中实现这一点?

对我来说,这是一个令人失望的解决方案,因为不确定的行为(也许你可以说服我相反),所以我搜索了另一个:

使用查找下一个可能的 PK 的嵌套选择语句插入。查找有关自己生成 PK 的其他答案我接近于使用该语句的工作解决方案(省略了从字符串到 int 的强制转换):

INSERT INTO mytable (pk,othercolumns)
VALUES(
(SELECT MIN(empty_numbers.empty_number) 
  FROM (SELECT t1.pk + 1 as empty_number 
    FROM mytable t1

    LEFT OUTER JOIN mytable t2
    ON t1.pk + 1  = t2.pk

    WHERE t2.pk IS NULL
    AND t1.pk > 100000)
  as empty_numbers),
 othervalues);

这就像一个魅力,并且(afaik)比我的第一种方法更可预测和稳定的解决方案,但是:我怎么可能从该语句中检索生成的 PK?我读过没有办法直接返回插入的行(或任何列),而且我发现的大多数谷歌结果都指向返回生成的密钥——即使我的密钥是生成的,它也不是由 DBMS 生成的直接,但根据我的声明。

注意,开发中使用的 DBMS 是 MSSQL 2008,而生产系统目前是 AS/400 上的 DB2(不知道哪个版本),所以我必须遵守 SQL 标准。我无法以任何方式更改数据库结构(例如,使用生成的密钥,我不确定存储过程)。

【问题讨论】:

  • 查看OUTPUT clause(我认为这是特定于 SQL Server)。
  • 是的,但是 sql server 的细节是不可能的 :-(
  • 为什么不使用将在生产中使用的相同数据库系统进行开发? (顺便说一句,从技术上讲,由于 IBM 自 2000 年以来就没有发布过 AS/400,所以几乎可以肯定您使用的是更新的 iSeries 或 Power System。操作系统的当前版本称为 IBM i 7.1。)您的 DB2 系统管理员(或“安全官”)应该能够轻松地为您创建一个测试模式(“库”),如果您自己不能这样做的话。为什么您的 PK 值是数字的字符串表示形式?您是否保证只有一个用户进程连接到您的数据库,可以将行插入此表?
  • @WarrenT 你的观点是正确的(是的,它是 iSeries 上的 DB2),但不幸的是,我对生产系统没有影响,范围内没有真正的数据库管理员或安全官员,唯一的我们的直接客户提供的测试数据是 mssql 备份,我知道至少有 5 个应用程序使用该表,恕我直言,字符串表示是某种我无法改变的历史错误(该项目中甚至发生了更奇怪的事情尤其是那个分贝,我只是不想为此生气)

标签: java sql sql-server db2 ibm-midrange


【解决方案1】:

DB2 for i 允许生成密钥、存储过程、用户定义的函数——几乎所有 SQL Server 可以做的事情。确切的实现是不同的,但这就是手册的用途:-) 询问您的管理员他们正在运行什么版本的 IBM i,然后点击Infocenter 了解详情。

限制因素是您不能更改数据库设计;在回填现有密钥空间中的“漏洞”时,您显然被多个进程尝试插入。这是一个很难破解的坚果。因为您无法更改数据库设计,所以除了允许和处理 PK 冲突外,没有什么可做的。没有任何 SQL 技巧会有所帮助 - SQL 方法是让数据库生成 PK,而不是应用程序。

如果允许进行一些更改,有几个备选方案可供建议。所有人都存在需要解决方法的问题,但由于应用程序设计的原因,目前这是不可避免的。

  1. 创建所有 INSERT 客户端用来检索下一个可用 PK 的 UDF。使用“可用号码”表并在发出时将其删除。
  2. 预先插入所有可用的号码。强制客户端执行更新。让他们 FETCH...FOR UPDATE where(其余数据 = 未填充)。这将锁定行,避免冲突,并使 PK 立即可用。
  3. 让 DB 和其他使用此表的应用程序保持原样,但让您的 INSERT 进程从预留给您使用的密钥块中提取。将下一个可用编号保存在 SQL SEQUENCE 或 IBM i 数据区域中。这仅在密钥空间中有一个尚未使用的非常大的洞时才有效。

【讨论】:

  • 嘿,我的评论有点晚了,只是想感谢您的回答和提示。我认为您的建议方式是正确的,所以我会接受您的回答。在我的解决方案中,我退出了单个查询并更改了 transaction_level(可序列化)并在该事务中使用了两个语句(仍然害怕锁,但到目前为止测试工作良好,即使两个语句之间存在异常)
猜你喜欢
  • 2012-08-22
  • 2010-09-21
  • 1970-01-01
  • 2011-11-18
  • 2017-10-10
  • 2019-04-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多