如何找出更新表的包/程序?

时间:2010-01-05 01:37:05

标签: plsql

我想知道是否有可能找出包中的哪个包或程序正在更新表?

由于某个项目被移交(移交项目的人已经离开)没有适当的文档,我们知道我们更新的数据总是回到某个奇怪的源点。

我们猜测这可能是在我们不知情的情况下运行update命令的数据库作业或调度程序。我希望有一种方法可以找出源代码调用的位置,即更新表并将源作为触发器插入到我们正在监视的表中。

有什么想法吗?

感谢。

4 个答案:

答案 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_DEPENDENCIES查看哪些包/触发器等使用该表。取决于系统的大小,可能会返回大量物体进行拖网。
  • 搜索所有数据库源代码以获取对表的引用,如下所示:

    选择不同的类型,名称 来自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;
/