尝试向表中插入数据时,可能会由于违反唯一键或违反外键等各种原因而失败。
我可以使用 DUP_VAL_ON_INDEX 异常来知道违反了唯一键,但是如果有多个带有唯一标记的列,我怎么知道哪个键?在这种情况下我应该使用触发器吗?
(我使用异常来驱动存储过程的流程,而不是查询表以确保插入的数据正常。)
尝试向表中插入数据时,可能会由于违反唯一键或违反外键等各种原因而失败。
我可以使用 DUP_VAL_ON_INDEX 异常来知道违反了唯一键,但是如果有多个带有唯一标记的列,我怎么知道哪个键?在这种情况下我应该使用触发器吗?
(我使用异常来驱动存储过程的流程,而不是查询表以确保插入的数据正常。)
这不是触发器的用途。请记住,您无法在触发器中查询目标表,因此您无法真正检查是否存在唯一性违规。我想说触发器的最佳理由是确保行填充有默认数据,例如序列中的 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;
对于它的价值,我建议您不要仅仅依靠应用程序代码来强制执行唯一性。一个关键(双关语)原因是很难编写正确执行此操作的代码,特别是对于在多个事务中同时插入或更新数据的情况。声明数据库将正确有效地强制执行的唯一约束相对简单。
如果您想知道哪些列违反了唯一约束,一种方法(我意识到这不是“漂亮”)是:
SQLERRM
)。如果您想知道哪些行违反了唯一约束,您可以使用语句的LOG ERRORS INTO
子句将有问题的INSERT
行记录到错误记录表中,如本示例中所述。
如果您想知道哪些行和列违反了唯一约束,您可以结合这两种方法——使用LOG ERRORS INTO
子句记录行,然后从错误消息中解析约束名称(也记录在错误日志中表)并查找相应的列。
话虽如此,您不一定需要在以下之间进行选择:a)首先检查数据并仅在通过检查时插入数据,以及 b)尝试插入数据,然后报告和/或处理任何错误。
异常(来自约束)或触发器?我说也不是。
我强烈建议使用应用程序代码和查询来驱动应用程序的逻辑,而不是触发器和约束。将逻辑与错误条件处理分开只会让代码的行为显得更加神奇。
尽管约束作为备份计划非常有用(在应用程序逻辑错误或更改的情况下),但您是正确的,因为它们不会以您想要的方式帮助处理运行时错误(即不会帮助您公开重复的键该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 来知道一行是否已经存在。
但是,这仍然不会为您提供违反键的值。您将不得不编写查询来找出哪些会违反约束。