0

我有一种情况我找不到解释,就在这里。(我将使用假设信息,因为原始信息非常大。)

我有一张桌子,让我们说:

table_a
-------------
name
last name
dept
status
notes

并且这个表有一个insert触发器,它对info做了大量的验证,根据验证的结果来改变新记录的status字段,其中一些验证是:

- check for the name existing in a dictionary
- check for the last name existing in a dictionary
- check that fields (name,last name,dept) aren't already inserted in table_b
- ... and so on

问题是,如果我通过查询在表上插入,比如

insert into table_a 
(name,last_name,dept,status,notes) 
values
('john','smith',1,0,'new');

完成所有验证过程、更新状态字段并在表中插入记录只需要 173 毫秒。(验证过程通过索引进行所有搜索)

但是如果我通过 SQLloader 尝试这个,读取一个包含 5000 条记录的文件,验证和插入 149 条记录需要大约 40 分钟(当然我杀了它......)

所以我尝试加载禁用触发器的数据(以检查速度),我得到它像所有记录一样在不到 10 秒的时间内加载。

所以我的问题是,我能做些什么来改进这个过程?我唯一的理论是我可能会使数据库饱和,因为它加载速度如此之快并启动了许多触发器实例,但我真的不知道。

我的目标是加载大约 60 个包含信息的文件,并通过触发器中的过程验证它们(尽管愿意尝试其他选项)。

我真的很感激你能提供的任何帮助!

补充------------------------------------------------- --------------------------------

感谢您的回答,现在我将阅读所有相关信息,现在希望您能在这部分帮助我。让我解释一下我需要的一些功能(我使用了触发器,因为我想不出其他任何东西)

所以表数据带有这个(重要的)字段:

pid name lastname birthdate dept cicle notes

数据是这样的

name lastname birthdate dept

现在,触发器对数据执行此操作:

  1. 调用函数来计算 pid(根据姓名、姓氏和生日用算法计算)

  2. 调用一个函数来检查字典上的名字(那是因为在我的字典中我有一个名字,这意味着如果一个人被命名为 john aaron smith jones 该函数将 john aaron 一分为二,并在字典中搜索 john 和 aaron单独的查询,这就是为什么我不使用外键 [以避免有很多组合 john aaron、john alan、john pierce..etc])--->有点坚持如何用键实现这个而不改变字典...也许用 CHECK?,姓氏外键是个好主意。

  3. 根据部门和当前日期从另一个表中获取 cicle(因为一个人可以在同一个部门的表中出现两次但在不同的 cicle 中)--->我怎样才能以更有效的方式获取此 cicle 值做正确的搜索?

  4. 最后,在完成所有这些验证之后,我需要确切知道未满足哪个验证(因此字段注释),因此触发器连接所有失败验证的字符串,如下所示:

    lastname not in dictionary, cannot calculate pid (invalid date), name not in dictionary

我知道,如果不满足约束检查,我所能做的就是将记录插入另一个表中并显示约束失败错误消息,但这只会让我得到一个验证,对吗?但我需要验证所有这些并将报告发送给其他部门,以便他们可以查看数据并对其进行所有必要的调整。

无论如何,这是我现在的情况,我会探索可能性,希望你能分享一些关于整个过程的信息,非常感谢你的时间。

4

1 回答 1

3

您已经完成了解决方案的一半:

“所以我尝试加载禁用触发器的数据(以检查速度)......它在不到 10 秒内加载所有记录。”

这并不奇怪。您当前的实现为插入到表 B 中的每一行执行大量单行 SELECT 语句。这将不可避免地给您带来糟糕的性能配置文件。SQL 是一种基于集合的语言,在多行操作中表现更好。

因此,您需要做的是找到一种方法来替换所有更有效的选择语句。然后,您将能够永久删除触发器。例如,用表 A 列和引用表之间的外键替换字典上的查找。关系完整性约束,作为内部 Oracle 代码,比我们可以编写的任何代码执行得都要好(并且也可以在多用户环境中工作)。

如果表 B 中已存在列组合,则不插入表 A 的规则更成问题。不是因为它很难做到,而是因为它听起来像糟糕的关系设计。如果您不想在表 B 中已经退出时加载表 A 中的记录,为什么不直接加载到表 B 中?或者也许你有一个应该从表 A表 B 中提取并形成表 C 的列子集(这将与 A 和 B 具有外键关系)?

无论如何,将这一点放在一边,您可以通过将 SQL*Loader 替换为外部表来使用基于集合的 SQL 来执行此操作。外部表允许我们将 CSV 文件呈现给数据库,就好像它是一个常规表一样。这意味着我们可以在普通的 SQL 语句中使用它。 了解更多。

因此,通过字典和外部表的外键约束,您可以用此语句替换 SQL 加载器代码(取决于包含在“...等”中的任何其他规则):

insert into table_a
select ext.* 
from external_table ext
     left outer join table_b b
     on (ext.name = b.name and ext.last_name = b.last_name and ext.dept=b.dept)
where b.name is null
log errors into err_table_a ('load_fail') ;

这采用 DML 错误日志记录语法以基于集合的方式捕获所有行的约束错误。 了解更多。它不会为表 B 中已存在的行引发异常。您可以使用多表 INSERT ALL将行路由到溢出表中,或者在事件之后使用 MINUS 设置操作来查找外部表中的行'不在表 A 中。取决于您的最终目标以及您需要如何报告事情。

也许比您预期的更复杂的答案。Oracle SQL 是一个非常广泛的 SQL 实现,具有许多提高批量操作效率的功能。阅读《概念指南》和《SQL 参考》以了解我们可以使用 Oracle 做多少事情真的很值得。

于 2013-07-03T03:53:08.567 回答