0

以下の PL/SQL ストアド プロシージャを作成して、Oracle11g データベース全体で文字列 (srchstr) を検索し、その文字列が見つかったテーブルと列を VALUESEARCHRESULTS というテーブルに返します。

この手順は、ユーザーとしてSQL Developerを介してOracle XEで正常に実行されました。ただし、スキーマ ABC の Oracle11g でユーザー SYS として実行しようとすると、次のエラーが表示されます。

ORA-00911: 無効な文字です 原因: 識別子は、文字と数字以外のASCII文字で開始することはできません。$#_ も最初の文字の後に使用できます。二重引用符で囲まれた識別子には、二重引用符以外の任意の文字を含めることができます。代替引用符 (q"#...#") では、区切り文字としてスペース、タブ、または改行を使用できません。他のすべてのコンテキストについては、SQL 言語リファレンス マニュアルを参照してください。

これがなぜなのか誰か知っていますか?以下の私のコードを見てください。

CREATE OR REPLACE PROCEDURE ABC.FIND_STRING(p_str IN VARCHAR2) authid current_user is
  l_query    clob;
  srchstr    varchar2(30) := '';
  r_cname    varchar2(30) := '';
  l_case     clob;
  l_runquery boolean;
  l_tname    varchar2(30);
  l_cname    varchar2(30);
begin
   dbms_application_info.set_client_info( '%' || upper(p_str) || '%' );

   for x in (select * from user_tables)
   loop
       l_query := 'select ''' || x.table_name || ''', $$
                     from ' || x.table_name || '
                    where rownum = 1 and ( 1=0 ';
       l_case := 'case ';
       l_runquery := FALSE;
       for y in ( select *
                    from user_tab_columns
                   where table_name = x.table_name
                     and (data_type in('CHAR', 'DATE', 'FLOAT', 'NCHAR', 'NUMBER', 'NVARCHAR2', 'VARCHAR2' )
                       or data_type like 'INTERVAL%' or data_type like 'TIMESTAMP%' )
                )
       loop
           l_runquery := TRUE;
           l_query := l_query || ' or upper(' || y.column_name ||
                      ') like userenv(''client_info'') ';
           l_case := l_case || ' when upper(' || y.column_name ||
                     ') like userenv(''client_info'') then ''' ||
                     y.column_name || '''';
       end loop;
       if ( l_runquery )
       then
           l_case := l_case || ' else NULL end';
           l_query := replace( l_query, '$$', l_case ) || ')';
           begin
              execute immediate l_query into l_tname, l_cname;
              r_cname := l_cname;
              dbms_application_info.read_client_info(srchstr);
              insert into ABC.ValueSearchResults (resulttable, resultcolumn, searchstring) values (x.table_name, r_cname, srchstr);
               dbms_output.put_line
               ( srchstr || ' found in ' || l_tname || '.' || l_cname );
           exception
               when no_data_found then
                   dbms_output.put_line
                   ( srchstr || ' has no hits in ' || x.table_name );
           end;
          end if;
        end loop;
       end;

EDIT : 上記のストアド プロシージャはエラーなしでコンパイルされます。次のコードは、テーブルからストアド プロシージャに値を渡すことによって、ストアド プロシージャを実行します。以下のコードを実行すると、エラーが表示されます。

BEGIN
   FOR c IN (SELECT ControlValue FROM ABC.ControlValues) LOOP
       ABC.FIND_STRING(c.ControlValue);
   END LOOP;
END;
4

1 に答える 1

0

最初に提起した問題の解決策を見つけました。

エラーの原因: スキーマが指定されていません。ユーザー固有のテーブルのみを取得します。

ストアド プロシージャはユーザー ABC としてデプロイされたときに実行されますが、ストアド プロシージャをユーザー ABC として実行するとエラーが発生しました。複数のスキーマに同じテーブル名が存在するように見えました。したがって、OWNER 変数を追加すると、テーブル名に関連付けられたスキーマが指定され、エラーが解消されました。

さらに、プロシージャはもともと USER_TABLES を検索していました。これにより、結果が現在のスキーマのテーブルのみに制限されました。USER_TABLES を DBA_TABLES に置き換えることにより、ストアド プロシージャの検索はデータベースのすべてのテーブルにまたがっていました。

修正されたコードについては、以下を参照してください。

CREATE OR REPLACE
PROCEDURE FIND_STRING(
    p_str IN VARCHAR2) authid current_user
IS
  l_query CLOB;
  srchstr VARCHAR2(100) := '';
  r_cname VARCHAR2(100) := '';
  l_case CLOB;
  l_runquery BOOLEAN;
  l_tname    VARCHAR2(100);
  l_cname    VARCHAR2(100);
BEGIN
  dbms_application_info.set_client_info( '%' || upper(p_str) || '%' );
  FOR x  IN
  (SELECT *
  FROM dba_tables
  WHERE table_name    <> 'CONTROLVALUES'
  AND table_name      <> 'VALUESEARCHRESULTS'
  AND tablespace_name <> 'SYSTEM'
  AND tablespace_name <> 'SYSAUX'
  AND tablespace_name <> 'TEMP'
  AND tablespace_name <> 'UNDOTBS1'
  )
  LOOP
    l_query    := 'select ''' || x.owner || '.' || x.table_name || ''', $$                         
from ' || x.owner || '.' || x.table_name || '                         
where rownum = 1 and ( 1=0 ';
    l_case     := 'case ';
    l_runquery := FALSE;
    FOR y  IN
    (SELECT *
    FROM dba_tab_columns
    WHERE table_name = x.table_name
    AND owner        = x.owner
    AND (data_type  IN ( 'CHAR', 'DATE', 'FLOAT', 'NCHAR', 'NUMBER', 'NVARCHAR2', 'VARCHAR2' )
    OR data_type LIKE 'INTERVAL%'
    OR data_type LIKE 'TIMESTAMP%' )
    )
    LOOP
      l_runquery := TRUE;
      l_query    := l_query || ' or upper(' || y.column_name || ') like userenv    (''client_info'') ';
      l_case     := l_case || ' when upper(' || y.column_name || ') like userenv    (''client_info'') then ''' || y.column_name || '''';
    END LOOP;
    IF ( l_runquery ) THEN
      l_case  := l_case || ' else NULL end';
      l_query := REPLACE( l_query, '$$', l_case ) || ')';
      BEGIN
        EXECUTE immediate l_query INTO l_tname, l_cname;
        r_cname := l_cname;
        dbms_application_info.read_client_info(srchstr);
        INSERT
        INTO VALUESEARCHRESULTS
          (
            resulttable,
            resultcolumn,
            searchstring
          )
          VALUES
          (
            x.table_name,
            r_cname,
            srchstr
          );
        dbms_output.put_line ( srchstr || ' found in ' || l_tname || '.' || l_cname );
      EXCEPTION
      WHEN no_data_found THEN
        dbms_output.put_line ( srchstr || ' has no hits in ' || x.owner || '.' || x.table_name );
      END;
    END IF;
  END LOOP;
END;
于 2013-06-03T18:50:15.507 に答える