插入返回 IDENTITY 生成 ORA 00933

Posted

技术标签:

【中文标题】插入返回 IDENTITY 生成 ORA 00933【英文标题】:INSERT returning IDENTITY generating ORA 00933 【发布时间】:2015-09-19 15:17:24 【问题描述】:

我正在尝试将单个记录插入到表中并返回通过 ASP.net/Visual Studio 添加到记录中的序列号。但是,我收到了上面提到的错误。最初我认为我的错误是因为它认为我可能会返回多个记录,但即使在重写了几种方法之后,错误仍然存​​在。关于这个主题存在多个帖子,但它们似乎都围绕着插入多条记录的可能性。

我怀疑因为我正在使用“select... from dual”,它仍然认为我可以插入多个记录。我显然不需要“select...from dual”,只是我想使用 WHERE 子句来保证目标表中不存在该记录。

任何帮助或建议将不胜感激。谢谢。

INSERT INTO blatchildren
  (blatranscriptid, childactivityid, enrollmentDate, enrollmentStatus)
  SELECT 2,
                 'cours000000000004981',
                 to_date('1/1/2015 12:00:00 AM', 'MM/DD/YYYY HH:MI:SS AM'),
                 'E'
    from dual
   where 'cours000000000004981' not in (select childactivityid from blatchildren) 
   returning id
    into :identity

为了测试代码,我一直在 PL/SQL Developer 中运行以下代码:

declare identity number(2);
begin
INSERT INTO blatchildren
  (blatranscriptid, childactivityid, enrollmentDate, enrollmentStatus)
  VALUES( 2,
         'cours000000000004981',
         to_date('1/1/2015 12:00:00 AM', 'MM/DD/YYYY HH:MI:SS AM'),
         'E')
  returning id
    into identity;
end;

【问题讨论】:

INSERT..SELECT FROM DUAL..WHERE 不要混合,你不想重复使用唯一键,即使没有 from dual 仍然没有意义 @wero - 是的,多次。 @LukasEder - 我正在使用 PL/SQL 工具进行测试,但在 Visual Studio 中使用。 PLSQL Insert into with subquery and returning clause (Oracle)的可能重复 您愿意在没有 where 子句的情况下运行此 SQL(从 blatchildren 中选择 childactivityid)。相反,如果您运行 select childiactivity from blatchchildren where childactivityid='someid' 然后使用 0 的 sql rowcnt 来决定插入,或者 1 - 不插入。 【参考方案1】:

您不能在 PL/SQL 中将 RETURNING 子句与 INSERT .. SELECT 一起使用:

insert_into_clause
 values_clause [ returning_clause ]
| subquery 
 [ error_logging_clause ]

returning_clause 只能与values_clause 一起提供

见:https://docs.oracle.com/database/121/SQLRF/statements_9014.htm#SQLRF55051

更好的方法可能是将UNIQUE 约束添加到blatchildren(childactivityid)

【讨论】:

谢谢,但我不确定在 INSERT 发生之前约束如何防止重复。我查看了对“可能重复”的引用,并按照其中的链接指向 forall。这看起来可能是一个可能的答案,但对于需要条件 where 子句的简单查询来说似乎相当复杂。 “在 INSERT 发生之前” 为什么这很重要?或者更确切地说:为什么你的方法更受欢迎? 也许我误解了你的建议,但我宁愿阻止错误,也不愿编写错误处理程序来捕获它。我是不是误解了你的建议? @Trebor:这是常见的做法。您是否担心性能? 感谢您对 VALUES 子句的澄清。这为我澄清了语法!【参考方案2】:

我完全同意 Luka Eder 的回答,但如果您必须使用带有 return 子句的单个查询,它看起来像这样:

variable identity number;
BEGIN
INSERT INTO blatchildren
  (blatranscriptid, childactivityid, enrollmentDate, enrollmentStatus)
  VALUES ((SELECT 2 from dual where 'cours000000000004981' not in (select childactivityid from blatchildren)),
          (SELECT 'cours000000000004981' from dual where 'cours000000000004981' not in (select childactivityid from blatchildren)), 
          (SELECT to_date('1/1/2015 12:00:00 AM', 'MM/DD/YYYY HH:MI:SS AM') from dual where 'cours000000000004981' not in (select childactivityid from blatchildren)), 
          (SELECT 'E'  from dual where 'cours000000000004981' not in (select childactivityid from blatchildren)))
   returning blatranscriptid
    into :identity;
END;
/

这里的逻辑,正如 Luka 所说,您必须使用 VALUES 子句才能使 RETURNING 起作用。注意我放在那里的括号数。在 values 子句中进行选择是可能的,我个人从来没有这样写过东西,我相信我只会查询数据库两次。

附言这并不能解决太多问题,您仍然需要进行插入尝试。 但也许它可以节省添加另一个约束,可能你在 id 列上有一个非空约束。

【讨论】:

我说“p.s. 这并不能解决很多问题,您仍然需要尝试插入” - 简短的回答是 Lukas 的回答是关于 oracle 的限制是正确的。 罗伯特,谢谢。我什至没有考虑在 values 子句中使用 SELECT 。尽管它并不漂亮,但它可以防止错误发生,而不仅仅是围绕可能的错误进行编码。虽然我还没有尝试过,但我的猜测是对 values 子句中所有值的单个选择查询也可能有效。谢谢你的想法。

以上是关于插入返回 IDENTITY 生成 ORA 00933的主要内容,如果未能解决你的问题,请参考以下文章

如何在sql中插入记录时返回id(id为自动增长)

如何得到SqlServer的自增ID

为啥 SCOPE_IDENTITY() 不返回我插入的 ID?

如果使用多个插入语句,有啥方法可以使用 SCOPE_IDENTITY 吗?

php 使用 Scope_identity 返回 id

SCOPE_IDENTITY的用法