查找在特定日期更新的所有表

时间:2014-07-08 16:53:48

标签: sql oracle oracle-sqldeveloper

我正在使用Oracle数据库,并且我正在尝试查找在特定日期更新的所有表。跟踪更新的所有表都有一个名为DT_UPDATE的列。我一直在尝试这个:

SELECT * FROM 
(SELECT TABLE_NAME FROM ALL_TAB_COLUMNS WHERE COLUMN_NAME = 'DT_UPDATE')
WHERE DT_UPDATE = <date>

但是得到这个错误:

ORA-00904: "DT_UPDATE": invalid identifier
00904. 00000 -  "%s: invalid identifier"
*Cause:    
*Action:
Error at Line: 3 Column: 7

我也试过别名嵌套的Select子句。

2 个答案:

答案 0 :(得分:2)

正如@zaratustra所说,你必须使用动态SQL。你可以这样做:

set serveroutput on
declare
  counter number;
begin
  for r in (
    select owner, table_name
    from all_tab_columns
    where column_name = 'DT_UPDATE'
  ) loop
    execute immediate 'select count(*) from "'
      || r.owner || '"."' || r.table_name
      || '" where dt_update = :dt and rownum = 1'
    into counter
    using date '2014-07-07';

    if counter = 1 then
      dbms_output.put_line(r.table_name);
    end if;
  end loop;
end;
/

对于table_name中标识为owner的每个all_tab_columns(以及dt_update,为了完整性),会生成一个新的动态选择,格式如下:

select count(*) from "<owner>"."<table_name>"
where dt_update = date '2014-07-07'
and rownum = 1;

rownum = 1过滤器可以在找到匹配的行后立即停止查询;因为你说你想知道哪些表被更新了,而不知道有多少行或确切哪些行,如果一行匹配那么你真正需要知道的。因此,对于每个表,动态查询都会得到0或1。

对于任何至少有一行与日期匹配的表,这将使用dbms_output打印表名,因此必须使用set serveroutput on或SQL中的DBMS_OUTPUT面板启用它开发人员,或您最喜欢的客户等同。

如果我使用该列创建一些表,但只填充一个具有我正在寻找的日期的表:

create table tab1 (dt_update date);
create table tab2 (dt_update date);
create table tab3 (dt_update date);

insert into tab1 values (trunc(sysdate) - 1);
insert into tab2 values (trunc(sysdate));

...然后运行我的匿名块产生:

anonymous block completed
TAB1

显然,请使用您自己的目标日期。这假设您的日期字段不包含时间组件。如果确实如此,那么您需要将其转换为涵盖整天的范围。

你也可以把它变成一个以日期为参数的流水线函数;这也处理带有时间元素的日期字段:

create or replace function get_updated_tables(p_date date)
return sys.odcivarchar2list pipelined as
  counter number;
begin
  for r in (
    select owner, table_name
    from all_tab_columns
    where column_name = 'DT_UPDATE'
  ) loop
    execute immediate 'select count(*) from "'
      || r.owner || '"."' || r.table_name
      || '" where dt_update >= :dt1 and dt_update < :dt2'
      || ' and rownum = 1'
    into counter
    using p_date, p_date + interval '1' day;

    if counter = 1 then
      pipe row (r.table_name);
    end if;
  end loop;
end;
/

然后您可以使用以下方式查询:

select column_value from table(get_updated_tables(date '2014-07-07'));

COLUMN_VALUE                 
------------------------------
TAB1                           

正如您在评论中所说,动态SQL很有趣,但只应在必要时使用。生成的语句在执行之前无法解析,因此您可能在运行时之前不会发现语法或其他错误。还要确保对值(而不是对象名称)使用绑定变量以避免SQL注入。

答案 1 :(得分:2)

假设我们有三个带有字段dt_update的表,并且每个表都有一条记录(如果更多则无关紧要):

create table tt1 (
  dt_update date
);
insert into tt1 values (sysdate);
create table tt2 (
  dt_update date
);
insert into tt2 values (sysdate - 1);
create table tt3 (
  dt_update date
);
insert into tt3 values (sysdate - 2);

此PL / SQL匿名块仅打印表格的名称,这些名称的记录值dt_update列大于或等于今天:

declare
  type table_names_tp is table of user_tables.table_name%type index by binary_integer;
  table_names table_names_tp;
  l_res number(1);
  l_deadline date := to_date('2014-07-08', 'YYYY-MM-DD');
begin
  select table_name
    BULK COLLECT INTO table_names
    from user_tab_columns
   where lower(column_name) = 'dt_update'
  ;
  for i in table_names.first..table_names.last
  loop
    execute immediate 'select count(*) from dual where exists (select null from ' || table_names(i) || ' where dt_update >= :dead_line)'
       into l_res
      using l_deadline;
    if l_res = 1
    then 
      DBMS_OUTPUT.put_line('Table ' || table_names(i) || ' was updated after ' || l_deadline);
    end if;
  end loop;
end;

您可以使用此代码作为示例开始编写代码。
请注意保护自己免受SQL注入,不要(!)使用值的连接,而是始终使用绑定变量。它还可以帮助您在SGA中存储缓存的查询计划,应用程序将从SGA区域读取数据并执行软解析。