2

これが私の使用例です。入力は、任意の複雑さのOracle PL/SQL ステートメントを表す文字列です。これは (スクリプトではなく) 単一のステートメントであると想定できます。ここで、この入力文字列のいくつかのビットを書き換える必要があります

たとえば、テーブル名にはプレフィックスを付ける必要があり、列エイリアスを使用しない選択リスト内の集計関数にはデフォルトのエイリアスを割り当てる必要があります。

SELECT SUM(ABS(x.value)), 
TO_CHAR(y.ID,'111,111'),
y.some_col
FROM
tableX x,
(SELECT DISTINCT ID
FROM tableZ z
WHERE ID > 10) y
WHERE
...

になる

SELECT SUM(ABS(x.value)) COL1, 
TO_CHAR(y.ID,'111,111') COL2,
y.some_col
FROM
pref.tableX x,
(SELECT DISTINCT ID, some_col
FROM pref.tableZ z
WHERE ID > 10) y
WHERE
...

(免責事項:問題を説明するためだけに、ステートメントは意味をなさない)

集約関数はネストされている可能性があり、subSELECT は b_tch であるため、正規表現はあえて使用しません。実際、私は 80% の成功を達成しましたが、残りの 20% が必要です。

正しいアプローチは、文法とパーサーを使用することだと思います。私はc ++ ANTLR2をいじりました(ただし、文法やそのような助けを借りた解析についてはあまり知りません)。SQL ビットを取得する簡単な方法がわかりません。

list<string> *ssel = theAST.getSubSelectList(); // fantasy land

「解析の専門家」がこの問題をどのように追求するかについて、誰かがいくつかの指針を提供できますか? 編集:私はOracle 9iを使用しています。

4

2 に答える 2

2

これを使用できるかもしれません。select ステートメントを xml ブロッ​​クに変更します。

declare
    cl clob;
begin
    dbms_lob.createtemporary (
        cl,
        true
    );
    sys.utl_xml.parsequery (
        user,
        'select e.deptno from emp e where deptno = 10',
        cl
    );
    dbms_output.put_line (cl);
    dbms_lob.freetemporary (cl);
end;
/ 

<QUERY>
  <SELECT>
    <SELECT_LIST>
      <SELECT_LIST_ITEM>
        <COLUMN_REF>
          <SCHEMA>MICHAEL</SCHEMA>
          <TABLE>EMP</TABLE>
          <TABLE_ALIAS>E</TABLE_ALIAS>
          <COLUMN_ALIAS>DEPTNO</COLUMN_ALIAS>
          <COLUMN>DEPTNO</COLUMN>
        </COLUMN_REF>
        ....
        ....
        ....
</QUERY>

ここを参照してください: http://forums.oracle.com/forums/thread.jspa?messageID=3693276嫜

これで、この xml ブロッ​​クを解析するだけで済みます。

編集1:

残念ながら、OPのニーズを完全には理解していませんが、これが役立つことを願っています(たとえば、 query の列の「名前」を尋ねる別の方法ですselect count(*),max(dummy) from dual):

set serveroutput on

DECLARE
 c       NUMBER;
 d       NUMBER;
 col_cnt PLS_INTEGER;
 f       BOOLEAN;
 rec_tab dbms_sql.desc_tab;
 col_num NUMBER;

PROCEDURE print_rec(rec in dbms_sql.desc_rec) IS
BEGIN
  dbms_output.new_line;
  dbms_output.put_line('col_type = ' || rec.col_type);
  dbms_output.put_line('col_maxlen = ' || rec.col_max_len);
  dbms_output.put_line('col_name = ' || rec.col_name);
  dbms_output.put_line('col_name_len = ' || rec.col_name_len);
  dbms_output.put_line('col_schema_name= ' || rec.col_schema_name);
  dbms_output.put_line('col_schema_name_len= ' || rec.col_schema_name_len);
  dbms_output.put_line('col_precision = ' || rec.col_precision);
  dbms_output.put_line('col_scale = ' || rec.col_scale);
  dbms_output.put('col_null_ok = ');

  IF (rec.col_null_ok) THEN
    dbms_output.put_line('True');
  ELSE
    dbms_output.put_line('False');
  END IF;
END;

BEGIN
  c := dbms_sql.open_cursor; 
  dbms_sql.parse(c,'select count(*),max(dummy) from dual ',dbms_sql.NATIVE); 
  dbms_sql.describe_columns(c, col_cnt, rec_tab);

  for i in rec_tab.first..rec_tab.last loop
    print_rec(rec_tab(i));
  end loop;

  dbms_sql.close_cursor(c);
END;
/

(詳細については、こちらを参照してください: http://www.psoug.org/reference/dbms_sql.html )

OPは、クエリ内のテーブルのスキーマ名も変更できるようにしたいと考えています。それを達成するための最も簡単な言い方は、テーブル名をクエリしuser_tablesてSQLステートメントでそれらのテーブル名を検索し、プレフィックスを付けるか、'alter session set current_schema = ....'.

于 2009-10-23T17:24:44.473 に答える
1

SQL ステートメント文字列のソースが他のコーダーである場合は、変更が必要な部分が特別なエスケープ規則によってマークされているだけであると単純に主張することができます。次に、部分文字列の検索と置換を使用して、パッチが必要な場所を見つけることができます。

本当に任意の SQL 文字列があり、それらを適切にマークできない場合は、観察したように SQL 文字列を何らかの方法で解析する必要があります。XML ソリューションは確かに可能な方法の 1 つです。

もう 1 つの方法は、プログラム変換システムを使用することです。このようなツールは、言語インスタンスの文字列を解析し、AST を構築し、AST の分析と変換を実行してから、改訂された文字列を吐き出すことができます。

DMS Software Reengineering Toolkitはそのようなシステムです。PLSQL フロント エンド パーサーがあります。また、パターン指向の変換を使用して、必要と思われる書き換えを実行できます。選択項目を含むあなたの例:

domain PLSQL.
rule use_explicit_column(e: expression):select_item -> select_item
   "\e" -> "\e \column\(\e\)".

ルールを読むには、引用符で囲まれたものは、操作したいコンピューター言語の抽象ツリーを表していることを理解する必要があります。「ドメインPLSQL」というフレーズが言うことは、「PLSQLパーサーを使用して」引用符で囲まれた文字列の内容を処理することです。(DMS には、選択できる言語パーサーが多数あります)。「式」および「select_item」という用語は、目的の言語 (この場合は PLSQL など) からの文法構造です。PLSQL リファレンス マニュアルの路線図を参照してください。バックスラッシュは、ターゲット言語の構文ではなく、エスケープ/メタ情報を表します。

ルールが言うことは、おそらく同じ式\eと対応する列 ( \column(\e) )で構成されるselect_itemに変換することによって、\eのみで構成されるselect_itemである解析済み要素を変換することです。特定のテーブルの選択項目リスト内の位置に基づいています。選択項目の位置から対応する名前を決定できる列関数を実装する必要があります。この例では、目的の式を引数として受け入れる列関数を定義することを選択しました。式は実際には一致したツリーとして渡されるため、列関数はそれがツリー内のどこにあるかを判断できます。 抽象構文ツリーをたどってselect_itemsリストを作成します。

このルールは、選択した項目のみを処理します。関心のある他のさまざまなケースを処理するために、さらにルールを追加します。

変換システムが行うことは次のとおりです。

  • 対象の言語フラグメントを解析する
  • ASTを構築する
  • (AST パターン マッチングを実行することにより) 関心のある場所のパターン マッチを可能にしますが、ターゲット言語の表面構文を使用します。
  • 一致したパターンを他のパターンに置き換える
  • 任意の置換を計算します (AST として)
  • 変更された AST からソース テキストを再生成します。

ルールを書くことは必ずしも些細なことではありませんが、問題が提起されていると述べられている場合は必要なことです。

XML が推奨するソリューションは、そのような AST を構築する別の方法です。XSLT から多くのことを引き出すことができるかもしれませんが、優れたパターン マッチング プロパティはありません。私が知らないのは、XML に解析ツリーが完全に詳細に含まれているかどうかです。DMS パーサーは、任意の分析と変換を行う場合に必要になるため、設計上これを提供します。

于 2009-10-26T07:58:27.753 に答える