跳出查询以从存储过程中的单独 table 获取 where 子句
Step out of a query to get where clauses from a separate table, within a stored procedure
我有一个程序可以用来将行数输出到 .csv 文件,但我可能想使用的一些 where 子句包含在 table 中。如何使用它们为计数创造条件?
我尝试使用连接管道 select 来对抗包含 where 子句的 table 但我对语法和它们应该去哪里感到困惑,我相信这就是我需要的地方最有帮助。
这些是 table 中的列,其中包含一些我最终想在过程中使用的 where 子句。
SCHEMA, DATABASE, FULL_TABLE, DRIVER_TABLE, MAND_JOIN
值可能是这样的:
PROD, DB1, RLTSHP, BOB.R_ID, A.AR_ID = B.AR_ID
我写的程序如下:
create or replace procedure PROJECT is
--variables
l_dblink varchar2(100) := 'DB1';
ROW_COUNT number;
file_handle UTL_FILE.file_type;
BEGIN
utl_file.put_line(file_handle, 'OWNER,TABLE_NAME,ROW_COUNT');
--main loop
for rws in (select /*+parallel */ owner, table_name
from dba_tables@DB1 a
where table_name in (select table_name
from meta_table
where driver_table is not null
and additional_joins is null)
and a.owner in (select distinct schema
from meta_table c)
order by table_name)
loop
execute immediate 'select count(*) from ' ||rws.owner||'.'||rws.table_name || '@' || l_dblink into ROW_COUNT;
utl_file.put_line(file_handle,
rws.OWNER || ',' ||
rws.TABLE_NAME || ',' ||
ROW_COUNT);
end loop;
END PROJECT;
/
但是,不是上面反映的简单select count(*)
,我想找到一种方法将数据包含在meta_table中来构造使用[=38的"where"子句=] 连接以限制输出,这样我就不会计算所有行,而是计算符合我构建的连接条件的行。
例如,实际执行的计数将是这样的:
select count(*)
from PROD.RLTSHP@DB1 b,
BOB.R_ID@DB1 a
where A.AR_ID = B.AR_ID;
基本上我会使用 meta_table 中的条目构建查询。我想我可以用 concat 的 / 管道来做到这一点,但我不确定具体怎么做。
你能帮忙吗?
您还需要将简单语句扩展到 assemble 连接条件。一个问题是您必须给 tables 别名,这些别名与 additional_joins
中使用的别名相匹配,即 B
代表 FULL,A
代表 DRIVER。这些必须是您 META_TABLE 中所有行的标准,否则您将生成无效的 SQL。
create or replace procedure PROJECT is
l_dblink varchar2(100) := 'DB1';
ROW_COUNT number;
file_handle UTL_FILE.file_type;
v_sql varchar2(32767);
BEGIN
utl_file.put_line(file_handle, 'OWNER,TABLE_NAME,ROW_COUNT');
<< main_loop >>
for rws in (select mt.*
from dba_tables@DB1 db
join meta_table mt
on mt.driver_table = db.table_name
and mt.owner = db.owner
where mt.db_link = l_dblink
order by mt.table_name)
loop
-- simple query
v_sql := 'select count(*) from ' || rws.owner||'.'||rws.driver_table || '@' || l_dblink;
-- join query
if rws.additional_joins is not null
and rws.full_table is not null then
v_sql := v_sql|| ' b, '|| rws.full_table ||'@'||l_dblink|| ' a where ' ||rws.additional_joins;
end if;
-- uncomment this for debugging
--dbms_output.put_line(v_sql);
execute immediate v_sql into ROW_COUNT;
utl_file.put(file_handle,
rws.OWNER || ',' ||
rws.TABLE_NAME || ',' ||
utl_file.put_line(file_handle, ROW_COUNT);
end loop main_loop;
END PROJECT;
/
备注
我们必须使用一个变量来 assemble 语句,因为最后的 SQL 是以行的内容为条件的。这可以实现高效的调试,因为我们有可以显示的东西。动态 SQL 很难,因为它将编译错误转化为运行时错误。当我们看不到实际执行的代码时,诊断很困难。
我已经调整了您的驾驶查询,使联接更安全。
您在代码中使用的列名与您在 table 结构中使用的列名不一致。因此可能存在命名错误,您需要自己修复这些错误。
我保留了 Old Skool 隐式连接语法。我很想生成 ANSI 92 SQL (inner join ... on
),但不清楚 additional_joins
是否仅包含连接条件。
专业提示。而不是评论你的循环 - --main loop
- 使用实际的 PL/SQL 标签 - <<main_loop>>
这样你就可以 link 匹配的 end loop
语句,就像我在这个代码。
您可能想要添加的改进:
- 验证 FULL_TABLE 存在于目标数据库中
- 在 UTL_FILE 输出中包含 FULL_TABLE
- 验证 ADDITIONAL_JOIN 中引用的列是否有效(使用 DBA_TAB_COLUMNS,但这比较棘手,因为您必须从文本中解析列名)
- 担心ADDITIONAL_JOIN的内容是否有效和完整的加入条件
首先我不推荐使用PARALLEL hint。如果您有很多带有 PARALLEL 提示的查询,它可能会杀死您的数据库。
我假设第 MAND_JOIN 列意味着,我们总是在那里有价值。
create or replace procedure PROJECT is
lc_sql_template CONSTANT varchar2(4000) :=
'select count(*) ' || CHR(10) ||
' from #TableOwner.#TableName@DB1 b' || CHR(10) ||
' inner join #FullTableName@DB1 a ON #JoinCodition';
lv_row_count number;
lv_file_handle UTL_FILE.file_type;
lv_sql varchar2(32767);
BEGIN
utl_file.put_line(lv_file_handle, 'OWNER,TABLE_NAME,ROW_COUNT');
for rws in (select mt.*
from dba_tables@DB1 db
inner join meta_table mt
on mt.driver_table = db.table_name
and mt.owner = db.owner
where mt.driver_table is not null
and mt.additional_joins is null
order by mt.table_name)
loop
lv_sql := lc_sql_template;
lv_sql := replace(lv_sql, '#TableOwner' , rws.owner);
lv_sql := replace(lv_sql, '#TableName' , rws.driver_table);
lv_sql := replace(lv_sql, '#FullTableName' , rws.full_table);
lv_sql := replace(lv_sql, '#JoinCodition' , rws.mand_join);
$if $$DevMode = true $then -- I even recommand to log this all the time
your_log_package.info(lv_sql);
$end
execute immediate lv_sql into lv_row_count;
utl_file.put(lv_file_handle, rws.OWNER || ',' || rws.TABLE_NAME || ',' || lv_row_count);
end loop main_loop;
exception
when others then
your_log_package.error(lv_sql);
raise;
end PROJECT;
我有一个程序可以用来将行数输出到 .csv 文件,但我可能想使用的一些 where 子句包含在 table 中。如何使用它们为计数创造条件?
我尝试使用连接管道 select 来对抗包含 where 子句的 table 但我对语法和它们应该去哪里感到困惑,我相信这就是我需要的地方最有帮助。
这些是 table 中的列,其中包含一些我最终想在过程中使用的 where 子句。
SCHEMA, DATABASE, FULL_TABLE, DRIVER_TABLE, MAND_JOIN
值可能是这样的:
PROD, DB1, RLTSHP, BOB.R_ID, A.AR_ID = B.AR_ID
我写的程序如下:
create or replace procedure PROJECT is
--variables
l_dblink varchar2(100) := 'DB1';
ROW_COUNT number;
file_handle UTL_FILE.file_type;
BEGIN
utl_file.put_line(file_handle, 'OWNER,TABLE_NAME,ROW_COUNT');
--main loop
for rws in (select /*+parallel */ owner, table_name
from dba_tables@DB1 a
where table_name in (select table_name
from meta_table
where driver_table is not null
and additional_joins is null)
and a.owner in (select distinct schema
from meta_table c)
order by table_name)
loop
execute immediate 'select count(*) from ' ||rws.owner||'.'||rws.table_name || '@' || l_dblink into ROW_COUNT;
utl_file.put_line(file_handle,
rws.OWNER || ',' ||
rws.TABLE_NAME || ',' ||
ROW_COUNT);
end loop;
END PROJECT;
/
但是,不是上面反映的简单select count(*)
,我想找到一种方法将数据包含在meta_table中来构造使用[=38的"where"子句=] 连接以限制输出,这样我就不会计算所有行,而是计算符合我构建的连接条件的行。
例如,实际执行的计数将是这样的:
select count(*)
from PROD.RLTSHP@DB1 b,
BOB.R_ID@DB1 a
where A.AR_ID = B.AR_ID;
基本上我会使用 meta_table 中的条目构建查询。我想我可以用 concat 的 / 管道来做到这一点,但我不确定具体怎么做。
你能帮忙吗?
您还需要将简单语句扩展到 assemble 连接条件。一个问题是您必须给 tables 别名,这些别名与 additional_joins
中使用的别名相匹配,即 B
代表 FULL,A
代表 DRIVER。这些必须是您 META_TABLE 中所有行的标准,否则您将生成无效的 SQL。
create or replace procedure PROJECT is
l_dblink varchar2(100) := 'DB1';
ROW_COUNT number;
file_handle UTL_FILE.file_type;
v_sql varchar2(32767);
BEGIN
utl_file.put_line(file_handle, 'OWNER,TABLE_NAME,ROW_COUNT');
<< main_loop >>
for rws in (select mt.*
from dba_tables@DB1 db
join meta_table mt
on mt.driver_table = db.table_name
and mt.owner = db.owner
where mt.db_link = l_dblink
order by mt.table_name)
loop
-- simple query
v_sql := 'select count(*) from ' || rws.owner||'.'||rws.driver_table || '@' || l_dblink;
-- join query
if rws.additional_joins is not null
and rws.full_table is not null then
v_sql := v_sql|| ' b, '|| rws.full_table ||'@'||l_dblink|| ' a where ' ||rws.additional_joins;
end if;
-- uncomment this for debugging
--dbms_output.put_line(v_sql);
execute immediate v_sql into ROW_COUNT;
utl_file.put(file_handle,
rws.OWNER || ',' ||
rws.TABLE_NAME || ',' ||
utl_file.put_line(file_handle, ROW_COUNT);
end loop main_loop;
END PROJECT;
/
备注
我们必须使用一个变量来 assemble 语句,因为最后的 SQL 是以行的内容为条件的。这可以实现高效的调试,因为我们有可以显示的东西。动态 SQL 很难,因为它将编译错误转化为运行时错误。当我们看不到实际执行的代码时,诊断很困难。
我已经调整了您的驾驶查询,使联接更安全。
您在代码中使用的列名与您在 table 结构中使用的列名不一致。因此可能存在命名错误,您需要自己修复这些错误。
我保留了 Old Skool 隐式连接语法。我很想生成 ANSI 92 SQL (inner join ... on
),但不清楚 additional_joins
是否仅包含连接条件。
专业提示。而不是评论你的循环 - --main loop
- 使用实际的 PL/SQL 标签 - <<main_loop>>
这样你就可以 link 匹配的 end loop
语句,就像我在这个代码。
您可能想要添加的改进:
- 验证 FULL_TABLE 存在于目标数据库中
- 在 UTL_FILE 输出中包含 FULL_TABLE
- 验证 ADDITIONAL_JOIN 中引用的列是否有效(使用 DBA_TAB_COLUMNS,但这比较棘手,因为您必须从文本中解析列名)
- 担心ADDITIONAL_JOIN的内容是否有效和完整的加入条件
首先我不推荐使用PARALLEL hint。如果您有很多带有 PARALLEL 提示的查询,它可能会杀死您的数据库。
我假设第 MAND_JOIN 列意味着,我们总是在那里有价值。
create or replace procedure PROJECT is
lc_sql_template CONSTANT varchar2(4000) :=
'select count(*) ' || CHR(10) ||
' from #TableOwner.#TableName@DB1 b' || CHR(10) ||
' inner join #FullTableName@DB1 a ON #JoinCodition';
lv_row_count number;
lv_file_handle UTL_FILE.file_type;
lv_sql varchar2(32767);
BEGIN
utl_file.put_line(lv_file_handle, 'OWNER,TABLE_NAME,ROW_COUNT');
for rws in (select mt.*
from dba_tables@DB1 db
inner join meta_table mt
on mt.driver_table = db.table_name
and mt.owner = db.owner
where mt.driver_table is not null
and mt.additional_joins is null
order by mt.table_name)
loop
lv_sql := lc_sql_template;
lv_sql := replace(lv_sql, '#TableOwner' , rws.owner);
lv_sql := replace(lv_sql, '#TableName' , rws.driver_table);
lv_sql := replace(lv_sql, '#FullTableName' , rws.full_table);
lv_sql := replace(lv_sql, '#JoinCodition' , rws.mand_join);
$if $$DevMode = true $then -- I even recommand to log this all the time
your_log_package.info(lv_sql);
$end
execute immediate lv_sql into lv_row_count;
utl_file.put(lv_file_handle, rws.OWNER || ',' || rws.TABLE_NAME || ',' || lv_row_count);
end loop main_loop;
exception
when others then
your_log_package.error(lv_sql);
raise;
end PROJECT;