Более расширенное сравнение результатов запроса PL / SQL

#oracle #plsql

#Oracle #plsql

Вопрос:

Более широкая история

Я создаю автоматический сортировщик запросов моих учеников. Я буду запускать и останавливать контейнеры Docker для каждого учащегося и уже знаю, как построить систему вокруг него. Я отправлю запрос непосредственно в контейнер базы данных вместе с моим собственным правильным запросом.

Вопрос

Я хотел бы иметь 2 вещи при сравнении двух запросов:

  • флажок, который переключает «Порядок столбцов важен, да / нет»
  • флажок, который переключает «Порядок строк важен, да / нет»

Это то, что у меня есть для этой «основной» части.

 sqlplus -s system/oracle@localhost:1521/xe <<EOF
    set feedback off trimspool on;
    spool result.txt;
    
    (select * from cities) MINUS (select id, name from cities);

    spool off;
    exit;
EOF
 

Мне нужны некоторые предложения. Как бы вы сравнили два запроса, когда порядок столбцов не важен? Как насчет порядка строк, нужно ли мне принудительно упорядочивать в каждой задаче? Каков наилучший подход для предоставления более автоматической обратной связи, просто вывод ошибок компилятора?

Я буду признателен за каждый совет.

PS Я уже сделал свою долю поиска в Google, но без особой удачи для pl / sql.

Комментарии:

1. А? Не понимаю. вы ссылаетесь на таблицу table в операции МИНУС.

2. Слева от МИНУСА приведен пример кода моего ученика, а правая часть МИНУСА — моя (правильная). В этом примере, если у нас обоих одинаковый результирующий набор, file result.txt будет пусто. В противном случае наши результаты будут отличаться.

3. вам придется использовать одни и те же столбцы, иначе как МИНУС узнает, похожа ли строка. Я не думаю, что порядок строк необходим.

4. Оператор MINUS требует одинакового количества столбцов одного и того же типа в том же порядке, иначе он не будет работать. Вам нужно будет динамически создавать оператор с помощью регулярного выражения и использовать немедленное выполнение, если вы пытаетесь сделать это таким образом.

Ответ №1:

Вы можете использовать DBMS_SQL package для создания курсора и получения имен столбцов. Что касается порядка строк, моим первым предположением было использовать ROWNUM псевдоколоночный и после добавления его использования MINUS . Но проблема в том, что единственный способ гарантировать упорядочение — это использовать ORDER BY , и я не могу сгенерировать правильную ORDER BY часть во внешнем выборе после замены столбцов. Поместить этот столбец после SELECT подстроки плохо, потому что тогда им трудно управлять UNION . Вы можете попросить поместить некоторый фиктивный столбец в самый верхний SELECT , который затем вы можете заменить, ROWNUM as rn а затем продолжить. Или выполните оба курсора и попробуйте сравнить их строка за строкой.

Код, который я получаю, приведен ниже. Здесь вы можете попробовать это.

 declare
  v_sql_t varchar(32000) := 'select 1 as a, ''q'' as b from dual union all select 2, ''a'' from dual order by 1';
  v_sql_s varchar(32000) := 'select 1 as a, ''q'' as b from dual union all select 2, ''a'' from dual order by 2';
  v_cursor integer;
  v_col_count integer;
  v_cols_t dbms_sql.desc_tab;
  v_cols_s dbms_sql.desc_tab;
  
  v_cols_select varchar2(32000);
  v_select_keyword_begin integer;
  v_final_select varchar2(32000);
  
  v_col_order integer := 1;
  v_row_order integer := 1;
begin
  v_cursor := dbms_sql.open_cursor;
  dbms_sql.parse(v_cursor, v_sql_t, dbms_sql.native);
  dbms_sql.describe_columns(v_cursor, v_col_count, v_cols_t);
  dbms_sql.parse(v_cursor, v_sql_s, dbms_sql.native);
  dbms_sql.describe_columns(v_cursor, v_col_count, v_cols_s);
  
  --Check column order
  for i in 1..v_cols_t.count loop
    if v_col_order > 0 and v_cols_t(i).col_name != v_cols_s(i).col_name then
      dbms_output.put_line('Wrong column order. Should be: ' || v_cols_t(i).col_name || ', got ' || v_cols_s(i).col_name);
    end if;
    
    if i = 1 then
      v_cols_select := v_cols_t(i).col_name;
    else
      v_cols_select := v_cols_select || ',' || v_cols_t(i).col_name;
    end if;
    
  end loop;
  
  if v_row_order > 0 then
    v_cols_select := v_cols_select || ', rownum as rn';
  end if;
  
  v_final_select := 'with t as ( select ' || v_cols_select || ' from (' || v_sql_t || ' ) )' || chr(10)
    || ', s as ( select ' || v_cols_select || ' from (' || v_sql_s || ' ) )' || chr(10)
    || ', t_s as (select * from t minus select * from s)' || chr(10)
    || ', s_t as (select * from s minus select * from t)' || chr(10)
    || 'select ''T'' as src, t_s.* from t_s
    union all
    select ''S'' as src, s_t.* from s_t';
  
  dbms_output.put_line(v_final_select);
end;
/