Выйдите из запроса, чтобы получить предложения where из отдельной таблицы в рамках хранимой процедуры - PullRequest
0 голосов
/ 09 мая 2019

У меня есть процедура, которую я использую для вывода количества строк в файл .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;
/

Однако вместо простого select count(*), отраженного в приведенном выше, я хочу найти способ включить данные в meta_table, чтобы создать предложения "where", которые используют соединения таблиц, чтобы ограничить вывод, чтобы я не считал все строки, но строки, которые соответствуют критериям в построенном мною соединении.

Например, фактический счетчик будет выглядеть примерно так:

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

По сути, я бы строил запрос, используя записи в meta_table. Я думаю, что могу сделать это с помощью concat's / pipe, но я точно не знаю как.

Можете ли вы помочь?

Ответы [ 2 ]

1 голос
/ 09 мая 2019

Вам необходимо расширить простое утверждение, чтобы собрать критерии объединения.Единственный улов в том, что вы должны указать псевдонимы таблиц, которые соответствуют псевдонимам, используемым в 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;
/

Примечания

Мы должны использовать переменную для сборки оператора, поскольку окончательный SQL зависит от содержимого строки.Это позволяет эффективно отлаживать, потому что у нас есть что-то, что мы можем отобразить.Динамический SQL сложен, потому что он превращает ошибки компиляции в ошибки времени выполнения.Диагностика сложна, когда мы не можем увидеть фактически выполненный код.

Я изменил ваш запрос, чтобы сделать соединения более безопасными.

Имена столбцов, которые вы использовали в коде, не состоят из имен столбцов, которые вы использовали для структуры таблицы.Таким образом, могут быть ошибки именования, которые вам нужно исправить самостоятельно.

Я сохранил синтаксис неявного соединения Old Skool.Я испытывал желание сгенерировать ANSI 92 SQL (inner join ... on), но не ясно, что additional_joins будет содержать только критерии объединения.

Pro tip.Вместо комментирования ваших циклов - --main loop - используйте фактическую метку PL / SQL - <<main_loop>>, чтобы вы могли связать соответствующий оператор end loop, как я сделал в этом коде.

Улучшения, которые вы можете захотетьдобавить:

  • проверить, что FULL_TABLE существует в целевой базе данных
  • , включить FULL_TABLE в вывод UTL_FILE
  • проверить, что столбцы, на которые есть ссылки в ADDITIONAL_JOIN, действительны (используя DBA_TAB_COLUMNS, но этохитрее, потому что вам придется анализировать имена столбцов из текста)
  • беспокоиться о том, является ли содержимое ADDITIONAL_JOIN действительным и выполнено ли условие соединения
0 голосов
/ 09 мая 2019

Прежде всего, я не рекомендую использовать подсказку 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;
...