跳出查询以在存储过程中从单独的表中获取 where 子句

Posted

技术标签:

【中文标题】跳出查询以在存储过程中从单独的表中获取 where 子句【英文标题】:Step out of a query to get where clauses from a separate table, within a stored procedure 【发布时间】:2019-05-09 03:12:16 【问题描述】:

我有一个用于将行数输出到 .csv 文件的过程,但我可能想要使用的一些 where 子句包含在一个表中。如何使用它们为计数创造条件?

我尝试使用连接管道来选择包含 where 子句的表,但我对语法以及它们应该去哪里感到困惑,我相信这是我最需要帮助的地方。

这些是表中的列,其中包含我最终要在过程中使用的一些 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;
/

但是,我想找到一种方法将数据包含在 meta_table 中以构造使用表连接来限制输出的“where”子句,而不是上面反映的简单 select count(*)所有行,但符合我构建的联接中条件的行。

例如,实际执行的计数将是这样的:

select count(*)
  from PROD.RLTSHP@DB1 b,
       BOB.R_ID@DB1 a
 where A.AR_ID = B.AR_ID;

基本上我会使用元表中的条目来构建查询。我想我可以用 concat 的 / 管道做到这一点,但我不知道具体怎么做。

你能帮忙吗?

【问题讨论】:

【参考方案1】:

您还需要扩展您的简单语句来组装连接条件。一个问题是您必须为表提供与additional_joins 中使用的别名匹配的别名,即B 用于FULL,A 用于驱动程序。这些必须是 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;
/

备注

我们必须使用变量来组合语句,因为最终的 SQL 取决于一行的内容。这可以实现高效的调试,因为我们有一些可以显示的东西。动态 SQL 很难,因为它将编译错误转化为运行时错误。当我们看不到实际执行的代码时,诊断很困难。

我已调整您的驾驶查询以使连接更安全。

您在代码中使用的列名与您用于表结构的列名不一致。因此,您可能需要自己修复命名错误。

我保留了 Old Skool 隐式连接语法。我很想生成 ANSI 92 SQL (inner join ... on),但不清楚 additional_joins 是否只包含连接条件。

专业提示。不要注释你的循环 - --main loop - 使用实际的 PL/SQL 标签 - &lt;&lt;main_loop&gt;&gt; 这样你就可以链接匹配的 end loop 语句,就像我在这段代码中所做的那样。

您可能想要添加的改进:

验证目标数据库中是否存在 FULL_TABLE 在 UTL_FILE 输出中包含 FULL_TABLE 验证 ADDITIONAL_JOIN 中引用的列是否有效(使用 DBA_TAB_COLUMNS,但这比较棘手,因为您必须从文本中解析列名) 担心ADDITIONAL_JOIN的内容是否是一个有效的并且完整的连接条件

【讨论】:

谢谢!一件事,我得到:第 1 行的错误:ORA-00904:“B”。“END_DTE”:无效标识符引用连接条件之一时。我通过以下方式引用了这些:execute immediate' select /*+parallel / count() from ' ||rws.owner||'.'||rws.table_name || '@' || l_dblink||'一个'||','|| rws.driver_table || '@' || l_dblink||' b'||'其中'||rws.mandatory_join ||'和 '||rws.additional_joins 附加连接包含特殊字符,如下所示:B.END_DTE >= '01-OCT-2018' 你知道我是否需要以某种方式转义这些? 嗯,看起来 DRIVER_TABLE 中的表没有名为 END_DTE 的列。您需要根据 DBA_TAB_COLUMNS 对其进行验证。这也是一个问题,因为它是一个过滤器而不是一个连接条件,所以生成的查询将是表 A 和 B 的产物。 我已经使用dbms_output调试了代码,在循环中写的内容如下:select /*+parallel /count() from SCHEMA1.TABLE1@ DB1 a ,SCHEMA2.TABLE2@DB1 b where A.AR_ID = B.AR_ID and B.END_DTE >= '01-DEC-2018' 导致:第 2 行出现错误:ORA-00904:“B”。“END_DTE” : 无效的标识符 所以,我确实在驱动程序表中有一个错误条目,但现在,它肯定似乎是 B.END_DTE >= '01-DEC-2018' 部分......它必须需要一些引用什么的? 实际上 - 不,在您的帮助下,我已经猜到了。该表中不存在该列...仅此而已!太高兴了——现在已经整理好了。谢谢!! :-)【参考方案2】:

首先我不推荐使用 PARALLEL 提示。如果您将有大量带有 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;

【讨论】:

以上是关于跳出查询以在存储过程中从单独的表中获取 where 子句的主要内容,如果未能解决你的问题,请参考以下文章

如何构建查询以获取每个用户的最新行,其中第三个条件位于单独的表中?

在一个查询Access数据库中从具有不同字段的两个不同表中选择列

复合求和:我想创建一个复合查询,它从两个不同的表中获取两列的单独总和,然后对它们求和

表中的存储过程列表

oracle中从一张表中筛选出不再多个时间段内的时间

如何从存储过程的表中的列中获取输出参数