动态SQL,一次一列比较两个记录



场景:我们在Oracle数据库的某些表上设置了闪回。不时地,我们希望看到哪些字段从一行更改到另一行。我们当然可以目测,但那很容易出错。

所以我有了一个"聪明"的想法,尝试逐个行,将当前记录存储到一个记录变量中,并将先前的记录存储到另一个记录变量中。然后逐个字段比较每个字段,如果不同,则打印出字段名和值。像这样:

DECLARE CURSOR myflash IS SELECT * FROM myflashtable;
OLDRECORD myflashtable%ROWTYPE;
NEWRECORD myflashtable%ROWTYPE;
dynamic_statement varchar2(4000);
cursor colnames is select * from all_tab_columns where table_name = 'myflashtable';
begin
if not myflash%ISOPEN then
  open myflash;
end if;
fetch myflash into NEWRECORD;
while myflash%FOUND loop;
  for columnnames in colnames loop
      /* cobble together dynamic SQL along the lines of
         "if oldrecord.column_name != newrecord.column_name 
         then print some information``....end if;"
      */
         execute immediate dynamic_statement;
  end loop;
  OLDRECORD := NEWRECORD;
  fetch myflash into NEWRECORD;
end loop;
end;

显然这行不通。最初它给我"无效的SQL语句",我添加了开始/结束到动态SQL。当我尝试运行那个版本时,它给了我一个错误,因为它不知道旧/新记录。当我运行时不执行,而只是转储生成的SQL,它会遍历每个记录上的所有列,因此部分逻辑可以工作。

我很确定有更好的方法来做到这一点,或者可能使它工作。一种想法是做一些类似声明旧/新值变量的事情,然后使用动态SQL将旧/新记录字段移动到其中的每个:

EXECUTE IMMEDIATE 'oldvalue := OLDRECORD.'||columnnames.column_name;
EXECUTE IMMEDIATE 'newvalue := NEWRECORD.'||columnnames.column_name;
IF oldvalue != newvalue then
   /* print some stuff */
END IF:

,但当然,技巧是目标变量必须处理一堆不同类型的列——char、date等。所以需要有旧/新值变量的变体,以及处理这些变量的逻辑,这就变得不那么有趣了。

有什么更优雅的方法吗?我在网站上找了一遍,并没有找到任何我想要做的事情。

你做对了。但是这需要做更多的编程工作。在连接中将旧表和新表用正确的主键连接起来,然后循环遍历它。您可以使用DMBS_SQL包来构建动态游标并遍历表。

最新更新