0

尝试向表中插入数据时,可能会由于违反唯一键或违反外键等各种原因而失败。

我可以使用 DUP_VAL_ON_INDEX 异常来知道违反了唯一键,但是如果有多个带有唯一标记的列,我怎么知道哪个键?在这种情况下我应该使用触发器吗?

(我使用异常来驱动存储过程的流程,而不是查询表以确保插入的数据正常。)

4

3 回答 3

0

这不是触发器的用途。请记住,您无法在触发器中查询目标表,因此您无法真正检查是否存在唯一性违规。我想说触发器的最佳理由是确保行填充有默认数据,例如序列中的 id。触发器也会混淆代码,因为不清楚为什么插入的行与您的语句不同。

约束执行除了显而易见的功能之外的有用功能:优化器可以使用约束中的信息来判断执行计划。例如,如果优化器知道列是否为 NOT NULL,则可以计算出更有效的路由。

上面@Ben 给出的INSERT语句是一个很好的语句,你也可以将你的语句包装在一个匿名块中:

BEGIN
    ... application code ... 
    BEGIN
        INSERT INTO mytable (col1, col2) VALUES (val1, val2);
    EXCEPTION WHEN DUP_VAL_ON_INDEX THEN
        ... error handling code ...
    END;
    ... application continues...
END;
于 2012-11-27T01:35:01.603 回答
0

对于它的价值,我建议您不要仅仅依靠应用程序代码来强制执行唯一性。一个关键(双关语)原因是很难编写正确执行此操作的代码,特别是对于在多个事务中同时插入或更新数据的情况。声明数据库将正确有效地强制执行的唯一约束相对简单。

如果您想知道哪些违反了唯一约束,一种方法(我意识到这不是“漂亮”)是:

  1. 从错误消息中解析约束名称(例如,由 返回的SQLERRM)。
  2. 使用某种机制(如数据字典或您自己的表)来“查找”哪些列对应于约束。

如果您想知道哪些违反了唯一约束,您可以使用语句的LOG ERRORS INTO子句将有问题的INSERT行记录到错误记录表中,如本示例中所述。

如果您想知道哪些行列违反了唯一约束,您可以结合这两种方法——使用LOG ERRORS INTO子句记录行,然后从错误消息中解析约束名称(也记录在错误日志中表)并查找相应的列。

话虽如此,您不一定需要在以下之间进行选择:a)首先检查数据并仅在通过检查时插入数据,以及 b)尝试插入数据,然后报告和/或处理任何错误。

于 2012-11-27T14:58:32.317 回答
0

异常(来自约束)或触发器?我说也不是。

强烈建议使用应用程序代码和查询来驱动应用程序的逻辑,而不是触发器和约束。将逻辑与错误条件处理分开只会让代码的行为显得更加神奇。

尽管约束作为备份计划非常有用(在应用程序逻辑错误或更改的情况下),但您是正确的,因为它们不会以您想要的方式帮助处理运行时错误(即不会帮助您公开重复的键该INSERT语句试图使用)。

正如您毫无疑问地确定的那样,您可以很容易地使用触发器来执行您所描述的操作,但最终这意味着您只是在触发器中隐藏了相同的逻辑测试(“这条记录是否已经存在?”)与您的INSERT语句将存在的应用程序代码相反。如果表上存在多个触发器,您可能还会经历不必要的执行开销,就好像一个会引发错误一样,任何其他先运行的触发器都是徒劳的。

如果您不想在运行新INSERT语句之前编写额外的语句来预先检查数据的条件,那么我建议使用这样的东西:

  INSERT INTO my_table (col1, col2)
  SELECT l_val1, l_val2
  FROM dual
  WHERE NOT EXISTS (
    SELECT 1
    FROM my_table t
    WHERE t.col1 = l_val1
    AND t.col2 = l_val2
  )

至少这样你就不会违反密钥,但你会通过测试SQL%ROWCOUNT是 0(没有插入行,所以必须已经存在)还是 1 来知道一行是否已经存在。

但是,这仍然不会为您提供违反键的值。您将不得不编写查询来找出哪些会违反约束。

于 2012-11-27T00:42:18.910 回答