4

私はPL/SQLコードを書いて、テーブルを非正規化し、クエリを実行しやすい形式にしました。このコードでは、一時テーブルを使用して作業の一部を実行し、元のテーブルのいくつかの行を一緒にマージします。

ロジックは、リンクされた記事のパターンに従って、パイプライン化されたテーブル関数として記述されます。表関数は、PRAGMA AUTONOMOUS_TRANSACTION宣言を使用して一時表操作を許可し、カーソル入力パラメーターを受け入れて非正規化を特定の ID 値に制限します。

次に、テーブル関数をクエリするためのビューを作成し、考えられるすべての ID 値をカーソルとして渡します (関数の他の使用法はより制限されます)。

私の質問: これは本当に必要ですか? 同じことを達成するためのはるかに簡単な方法を完全に見逃していませんか?

PL/SQL に触れるたびに、入力しすぎているような印象を受けます。

更新:私が扱っているテーブルのスケッチを追加して、私が話している非正規化のアイデアをすべての人に提供します。このテーブルには、従業員の仕事の履歴が格納され、それぞれにアクティブ化行と (場合によっては) 終了行があります。従業員が複数の仕事を同時に持つことも、連続していない日付範囲で何度も同じ仕事をすることも可能です。例えば:

| EMP_ID | JOB_ID | STATUS | EFF_DATE    | other columns...
|      1 |     10 | A      | 10-JAN-2008 |
|      2 |     11 | A      | 13-JAN-2008 |
|      1 |     12 | A      | 20-JAN-2008 |
|      2 |     11 | T      | 01-FEB-2008 |
|      1 |     10 | T      | 02-FEB-2008 |
|      2 |     11 | A      | 20-FEB-2008 |

それを照会して、誰がいつどの仕事で働いているかを把握することは自明ではありません。EMP_IDしたがって、私の非正規化関数は、カーソルを介して渡されたすべての s について、各ジョブの日付範囲のみを一時テーブルに入力します。s 1 と 2 を渡すEMP_IDと、次のようになります。

| EMP_ID | JOB_ID | START_DATE  | END_DATE    |
|      1 |     10 | 10-JAN-2008 | 02-FEB-2008 |
|      2 |     11 | 13-JAN-2008 | 01-FEB-2008 |
|      1 |     12 | 20-JAN-2008 |             |
|      2 |     11 | 20-FEB-2008 |             |

(終了日が事前に決められていないジョブの をEND_DATE許可します。)NULL

ご想像のとおり、この非正規化形式はクエリがはるかに簡単ですが、それを作成するには (私が知る限りでは) 中間結果 (たとえば、アクティベーション行が実行されたジョブ レコード) を格納するための一時テーブルが必要です。見つかりましたが、終了ではありません...まだ)。パイプライン化されたテーブル関数を使用して一時テーブルにデータを入力し、その行を返すことは、私がその方法を理解した唯一の方法です。

4

6 に答える 6

4

これにアプローチする方法は、分析関数を使用することだと思います...

以下を使用してテストケースをセットアップしました。

create table employee_job (
    emp_id integer,
    job_id integer,
    status varchar2(1 char),
    eff_date date
    );  

insert into employee_job values (1,10,'A',to_date('10-JAN-2008','DD-MON-YYYY'));
insert into employee_job values (2,11,'A',to_date('13-JAN-2008','DD-MON-YYYY'));
insert into employee_job values (1,12,'A',to_date('20-JAN-2008','DD-MON-YYYY'));
insert into employee_job values (2,11,'T',to_date('01-FEB-2008','DD-MON-YYYY'));
insert into employee_job values (1,10,'T',to_date('02-FEB-2008','DD-MON-YYYY'));
insert into employee_job values (2,11,'A',to_date('20-FEB-2008','DD-MON-YYYY'));

commit;

リード関数を使用して次の日付を取得し、すべてをサブクエリとしてラップして、「A」レコードを取得し、終了日があればそれを追加しました。

select
    emp_id,
    job_id,
    eff_date start_date,
    decode(next_status,'T',next_eff_date,null) end_date
from
    (
    select
        emp_id,
        job_id,
        eff_date,
        status,
        lead(eff_date,1,null) over (partition by emp_id, job_id order by eff_date, status) next_eff_date,
        lead(status,1,null) over (partition by emp_id, job_id order by eff_date, status) next_status
    from
        employee_job
    )
where
    status = 'A'
order by
    start_date,
    emp_id,
    job_id

私が見逃したいくつかのユースケースがあると確信していますが、あなたはその考えを理解しています. 分析関数はあなたの友達です:)

EMP_ID   JOB_ID     START_DATE     END_DATE            
  1        10       10-JAN-2008    02-FEB-2008         
  2        11       13-JAN-2008    01-FEB-2008         
  2        11       20-FEB-2008                              
  1        12       20-JAN-2008                              
于 2008-09-18T21:19:12.863 に答える
1

ここでの本当の問題は、「書き込み専用」のテーブル設計です。つまり、テーブルにデータを挿入するのは簡単ですが、そこから有用な情報を取得するのは難しく、非効率的です! 「一時」テーブルには、「永続」テーブルが最初に持つべき構造があります。

おそらくこれを行うことができますか:

  • より良い構造の永久テーブルを作成する
  • 最初のテーブルのデータと一致するように入力します
  • 元のテーブルにデータベース トリガーを定義して、今後新しいテーブルの同期を維持する

その後、新しいテーブルから選択してレポートを実行できます。

于 2008-09-19T15:33:44.390 に答える
1

入力パラメーターをカーソルとして持つのではなく、テーブル変数を使用するか (Oracle にそのようなものがあるかどうかはわかりません)、別の一時テーブルに ID 値を入力し、ビューで結合します。 /function または必要な場所。

私の正直な意見では、カーソルを使用するのは、ループする必要あるときだけです。また、ループする必要がある場合は、アプリケーション ロジック内のデータベースの外で行うことを常にお勧めします。

于 2008-08-22T10:25:06.327 に答える
1

ここで読み取りの一貫性を提供しているように聞こえます。つまり、同時変更データの変更がある場合、一時テーブルの内容がソースデータと同期しなくなる可能性があります。

要件や、達成したいものの複雑さを知らなくても。私は試みます

  1. SQL の (おそらく複雑な) ロジックを含むビューを定義するには、それ以外の場合は、いくつかの PL/SQL をミックスに追加します。
  2. パイプライン化されたテーブル関数ですが、(一時テーブルの代わりに) SQL コレクション型を使用します。簡単な例を次に示します: http://asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:4447489221109

番号 2 では、可動部分が少なくなり、一貫性の問題が解決されます。

マシュー・バトラー

于 2008-09-18T12:57:26.140 に答える
0

HollyStyles、私はあなたにこれ以上同意できませんでした。私も以前はTSQLの担当者でしたが、Oracleの特異性のいくつかは少し戸惑う以上のものでした。残念ながら、一時テーブルはOracleではそれほど便利ではありません。この場合、他の既存のSQLロジックはテーブルを直接クエリすることを想定しているため、代わりにこのビューを提供します。このシステムには、データベースの外部に存在するアプリケーションロジックは実際にはありません。

Oracle開発者は、私が思っていたよりもはるかに熱心にカーソルを使用しているようです。PL / SQLの束縛と規律の性質を考えると、それはさらに驚くべきことです。

于 2008-08-22T15:36:36.080 に答える
0

最も簡単な解決策は次のとおりです。

  1. 必要な ID だけを含むグローバル一時テーブルを作成します。

    CREATE GLOBAL TEMPORARY TABLE tab_ids (id INTEGER)  
    ON COMMIT DELETE ROWS;
    
  2. 一時テーブルに必要な ID を入力します。

  3. プロシージャで EXISTS 操作を使用して、IDs テーブルのみにある行を選択します。

      SELECT yt.col1, yt.col2 FROM your\_table yt  
       WHERE EXISTS (  
          SELECT 'X' FROM tab_ids ti  
           WHERE ti.id = yt.id  
       )
    

ID のコンマ区切り文字列を関数パラメーターとして渡し、それをテーブルに解析することもできます。これは、単一の SELECT によって実行されます。もっと知りたい - 方法を聞いてください :-) しかし、それは別の質問でなければなりません。

于 2008-09-18T01:04:32.563 に答える