我正在使用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子句。
答案 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区域读取数据并执行软解析。