我想知道是否有可能找出包中的哪个包或程序正在更新表?
由于某个项目被移交(移交项目的人已经离开)没有适当的文档,我们知道我们更新的数据总是回到某个奇怪的源点。
我们猜测这可能是在我们不知情的情况下运行update命令的数据库作业或调度程序。我希望有一种方法可以找出源代码调用的位置,即更新表并将源作为触发器插入到我们正在监视的表中。
有什么想法吗?
感谢。
答案 0 :(得分:7)
更新:我四处寻找并发现了 如何追溯一个声明回到它 拥有PL / SQL对象。
结合Tony提到的内容,您可以创建一个记录表和一个如下所示的触发器:
CREATE TABLE statement_tracker
( SID NUMBER
, serial# NUMBER
, date_run DATE
, program VARCHAR2(48) null
, module VARCHAR2(48) null
, machine VARCHAR2(64) null
, osuser VARCHAR2(30) null
, sql_text CLOB null
, program_id number
);
CREATE OR REPLACE TRIGGER smb_t_t
AFTER UPDATE
ON smb_test
BEGIN
INSERT
INTO statement_tracker
SELECT ss.SID
, ss.serial#
, sysdate
, ss.program
, ss.module
, ss.machine
, ss.osuser
, sq.sql_fulltext
, sq.program_id
FROM v$session ss
, v$sql sq
WHERE ss.sql_address = sq.address
AND ss.SID = USERENV('sid');
END;
/
为了编译上面的触发器,当您以SYS用户身份登录时,您需要向触发器的所有者授予这些权限:
grant select on V_$SESSION to <user>;
grant select on V_$SQL to <user>;
您可能希望保护触发器中的insert语句,只有在您感兴趣的更改发生时才会使其记录 - 在我的测试服务器上,此语句运行得相当慢(1秒),所以我不想记录所有这些更新。当然,在这种情况下,您需要将触发器更改为行级触发器,以便您可以检查:new或:old值。如果您真的担心select的开销,可以将其更改为不加入v $ sql,而只是保存SQL_ADDRESS列,然后使用DBMS_JOB计划作业,并通过第二次更新更新sql_text列语句,从而将更新卸载到另一个会话,而不是阻止您的原始更新。
不幸的是,这只会告诉你一半的故事。您将要查看的语句将是最近端的语句 - 在这种情况下是更新 - 即使由启动它的进程执行的原始语句是存储过程。这是program_id列的来源。如果update语句是过程或触发器的一部分,program_id将指向相关代码的object_id - 您可以这样解决它:
SELECT * FROM all_objects where object_id = <program_id>;
如果更新语句是直接从客户端执行的,我不知道program_id代表什么,但你不需要它 - 你可以在“程序”列的“程序”列中找到可执行文件的名称。 statement_tracker。如果更新是从匿名PL / SQL块执行的,我不是如何追踪它 - 你需要进一步试验。
但是,osuser /机器/程序/模块信息可能足以让您指向正确的方向。
答案 1 :(得分:3)
如果它是预定的数据库作业,那么您可以找出存在的预定数据库作业并查看它们的作用。您可以做的其他事情是:
搜索所有数据库源代码以获取对表的引用,如下所示:
选择不同的类型,名称 来自all_source 其中lower(text)like lower('%mytable%');
同样可能返回很多对象,当然会出现一些“误报”,其中搜索字符串出现但实际上并不是对该表的引用。你甚至可以尝试更具体的东西,如:
select distinct type, name
from all_source
where lower(text) like lower('%insert into mytable%');
但当然会错过命令格式不同的情况。
此外,是否可以通过服务器上的“cron”作业运行SQL脚本?
答案 2 :(得分:1)
只需编写“更新后”触发器,在此触发器中,将“DBMS_UTILITY.FORMAT_CALL_STACK”的结果记录在专用表中。
此函数的目的是为您提供完整的调用堆栈,其中包含已触发的存储过程和触发器以访问您的代码。 我是从移动应用程序写的,所以我不能给你更详细的例子,但如果你谷歌为它,你会发现很多。
答案 3 :(得分:0)
如果你在本地工作,并且只对改变数据的第一件事感兴趣,那么快速而肮脏的选项是在触发器中抛出错误而不是记录。这样,您可以获得通常的堆栈跟踪,并且输入的次数更少,您无需创建新表:
AFTER UPDATE ON table_of_interest
BEGIN
RAISE_APPLICATION_ERROR(-20001, 'something changed it');
END;
/