выберите с переменным параметром в процедуре - PullRequest
0 голосов
/ 06 сентября 2018

Я пытаюсь написать процедуру со следующими функциями. А именно, ищет записи из таблиц в схеме. В частности, это столбец typepkstring в этих таблицах. В то же время у меня есть таблица составных типов в той же схеме, в которой есть столбец pk. Столбец pk содержит все числовые идентификаторы из вышеупомянутого столбца typepkstring. И теперь проблема в том, что в typepkstring у нас есть дополнительные ключи, которых нет в столбце pk в составных типах tabel. И мне нужно найти схему и выписать ее вместе с именем таблицы, в которой они находятся.

На данный момент моя процедура выглядит следующим образом:

    create or replace PROCEDURE SIEROT
      (i_table_name VARCHAR2)
      is
    CURSOR c is 
      SELECT DISTINCT i_table_name.TYPEPKSTRING
                        FROM  i_table_name
                        LEFT OUTER JOIN COMPOSEDTYPES
                        ON  i_table_name.TYPEPKSTRING=COMPOSEDTYPES.PK
                        WHERE COMPOSEDTYPES.PK IS NULL; 

   TYPE c_list IS TABLE of PRODUCTS.TYPEPKSTRING%type INDEX BY binary_integer; 
   TYPEPK_list c_list; 
   counter integer :=0; 
BEGIN 
   FOR n IN c LOOP 
      counter := counter +1; 
      TYPEPK_list(counter) := n.TYPEPKSTRING; 
      dbms_output.put_line('TABLE: '||i_table_name||'('||counter||'):'||TYPEPK_list(counter)); 
   END LOOP; 

END;

и звонят:

set serveroutput on
DECLARE
    ind integer := 0;
BEGIN
FOR ind IN (select table_name from all_tab_columns where column_name='TYPEPKSTRING' AND table_name!='COMPOSEDTYPES')
  LOOP
    BEGIN
        SIEROT(ind.table_name);
    EXCEPTION 
        WHEN NO_DATA_FOUND THEN
        null;
    END; 
  END LOOP;
END;

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

Procedure SIEROT compiled

LINE/COL  ERROR
--------- -------------------------------------------------------------
5/7       PL/SQL: SQL Statement ignored
6/31      PL/SQL: ORA-00942: table or view does not exist
17/7      PL/SQL: Statement ignored
17/31     PLS-00364: loop index variable 'N' use is invalid
Errors: check compiler log

То же самое, что и для постоянно вводимых имен таблиц, где я поместил 2 записи, которые соответствуют условиям задачи, работает правильно:

SELECT DISTINCT TESTOWY.TYPEPKSTRING
                        FROM TESTOWY
                        LEFT OUTER JOIN COMPOSEDTYPES
                        ON TESTOWY.TYPEPKSTRING=COMPOSEDTYPES.PK
                        WHERE COMPOSEDTYPES.PK IS NULL; 

и для select, введенного таким образом в процедуре, он дает ожидаемый эффект, но мне нужно параметризовать имя исходной таблицы, если она хочет выполнить поиск по всей всей схеме, а не только по конкретной. Только один из вышеупомянутых выборов будет достаточен для одного конкретного:

TABLE: TESTOWY(1):8790000000098
TABLE: TESTOWY(2):8790000000124


PL/SQL procedure successfully completed.

У меня действительно нет сил для этой процедуры. Напишите мне, как улучшить это, чтобы работать, но и выполнить свое мнение. Спасибо за любые подсказки или исправления;)

Ответы [ 2 ]

0 голосов
/ 06 сентября 2018

Вы не можете использовать значение параметра в качестве имени таблицы в запросе напрямую - вам нужно динамически построить оператор SELECT, а затем использовать цикл для извлечения данных:

CREATE OR REPLACE PROCEDURE SIEROT(i_table_name VARCHAR2) IS
  strSelect      VARCHAR2(32767);
  c              SYS_REFCURSOR;
  vTYPEPKSTRING  PRODUCTS.TYPEPKSTRING%TYPE;

  TYPE c_list IS TABLE of PRODUCTS.TYPEPKSTRING%type INDEX BY binary_integer; 
  TYPEPK_list c_list; 
  counter integer := 0; 
BEGIN 
  strSelect := 'SELECT DISTINCT i.TYPEPKSTRING ' ||
               '  FROM ' || i_table_name || ' i ' ||
               '  LEFT OUTER JOIN COMPOSEDTYPES c ' ||
               '    ON i.TYPEPKSTRING = c.PK ' ||
               '  WHERE c.PK IS NULL';

  OPEN c FOR strSelect;

  FETCH c INTO vTYPEPKSTRING;

  WHILE c%FOUND LOOP
    counter := counter + 1; 
    TYPEPK_list(counter) := vTYPEPKSTRING; 
    dbms_output.put_line('TABLE: '||i_table_name||'('||counter||'):'||TYPEPK_list(counter)); 

    FETCH c INTO vTYPEPKSTRING;
  END LOOP; 

  CLOSE c;
EXCEPTION
  WHEN OTHERS THEN
    IF c%ISOPEN THEN
      CLOSE c;
    END IF;
END SIEROT;

Удачи.

0 голосов
/ 06 сентября 2018

Здесь вам придется использовать динамический SQL (т. Е. execute immediate), так как имя таблицы (передаваемое в качестве параметра) нельзя использовать в запросе. С вашей точки зрения, кажется, что весь код в процедуре SIEROT должен быть динамический .

Вот пример, основанный на схеме Скотта (поскольку у меня нет ваших таблиц):

SQL> set serveroutput on
SQL> create or replace procedure sierot(i_table_name in varchar2)
  2  is
  3
  4    l_str varchar2(2000);
  5    l_str_2 varchar2(2000);
  6    counter integer := 0;
  7  begin
  8    l_str := 'select distinct i.empno typepkstring from ' || i_table_name || ' i join dept d on d.deptno = i.deptno
  9              where d.deptno = 10';
 10
 11    l_str_2 := 'declare
 12                  counter integer := 0;
 13                  type c_list is table of emp.empno%type index by binary_integer;
 14                  typepk_list c_list;
 15                begin
 16                  for n in (' || l_str ||') loop
 17                    counter := counter + 1;
 18                    typepk_list(counter) := n.typepkstring;
 19                    dbms_output.put_line(TYPEPK_list(counter));
 20                  end loop;
 21                end;';
 22
 23    execute immediate l_str_2;
 24
 25  end;
 26  /

Procedure created.

SQL> exec sierot('emp');
7782
7839
7934

PL/SQL procedure successfully completed.

SQL>
...