1

データベースの列を別の列の集計で自動的に更新したいのですが。関連するテーブルは3つあります。

T_RIDER
  RIDER_ID
  TMP_PONYLIST
  ...

T_RIDER_PONY
  RIDER_ID
  PONY_ID

T_PONY
  PONY_ID
  PONY_NAME
  ...

T_RIDERを介してn:mの関係T_PONYがありますT_RIDER_PONYT_RIDERさらにT_PONYいくつかの列がありますが、ここでのみTMP_PONYLIST関連PONY_NAMEします。

TMP_PONYLISTのセミコロンで区切られたリストです。のPONY_NAMESようなものを想像してください"Twisty Tail;Candy Cane;Lucky Leaf"。何が起こっても、このフィールドを最新の状態に保ちたいと思いT_RIDER_PONYますT_PONY

私の最初のアイデアは、トリガーをオンにすることでしT_RIDER_PONYT_PONYT_RIDER_PONY問題は、トリガー内で読み取ることが不可能であるように思われることです。私は常にを取得しORA-04091ます。3つのトリガーとパッケージ変数を操作するためのヒントをいくつか見つけましたが、これは非常に複雑に聞こえます。

TMP_PONYLISTスキーマを変更するか、完全に削除したほうがいいと思うかもしれません。これらはオプションですが、この質問のトピックではありません。今のところ、アプリケーションに変更を加える必要のない回答にのみ関心があります(アプリケーションはテーブルを直接操作せず、ビューのみを操作するため、ビューを使ったトリックは許可されます)。

TMP_PONYLISTでは、どうすれば自動的に最新の状態に保つことができますか?文字列を連結する方法は興味深いサブ問題であり、私もまだエレガントな解決策を見つけていません。

Oracle Database 10gEnterpriseEditionリリース10.2.0.4.0-64biを使用しています。

アップデート

マテリアライズドビューを使用するというアイデアが好きです。私が持っているのは:

CREATE MATERIALIZED VIEW
  V_TMP_PONYLIST 
BUILD IMMEDIATE
REFRESH COMPLETE ON COMMIT
AS SELECT 
  R.RIDER_ID, string_agg(P.PONY_NAME) AS TMP_PONYLIST
FROM
  T_PONY P, T_RIDER R, T_RIDER_PONY RP
WHERE 
  P.PLOTGROUP_ID=RP.PLOTGROUP_ID AND
  R.QUEUE_ID=RP.QUEUE_ID 
GROUP BY R.RIDER_ID;

string_agg長いので表示されていませんが、関係ないと思います。

でコンパイルされませんON COMMIT、私は取得しORA-12054ます。ドキュメントの集計はでのみ禁止されていることを理解している REFRESH FASTので、ここでの問題は何ですか?

更新 VincentsとTonysの回答は異なっていましたが、どちらも役に立ちました。Tonysを受け入れましたが、Vincentsの回答も必ず読んでください。

4

2 に答える 2

3

列の情報TMP_PONYLISTは冗長です(他の場所に存在します)。それを維持するためにあらゆる種類の問題が発生します(何らかのロックメカニズムがない限り、マルチユーザー環境では解決策は正しく機能しません)。

正規化されたモデルでは、この列を物理モデルから削除するだけです。情報が必要な場合は、たとえばOracle11gR2でビューを使用できます。

CREATE OR REPLACE VIEW rider_v AS
SELECT rider_id, /*...,*/
       (SELECT listagg(p.pony_name, ';') WITHIN GROUP (ORDER BY p.pony_name)
          FROM t_pony p
          JOIN t_rider_pony rp ON (p.pony_id = rp.pony_id)
         WHERE rp.rider_id = r.rider_id) tmp_ponylist
  FROM t_rider r;

11gR2より前の文字列集約の例については、このSOを参照してください。

于 2009-09-22T12:04:40.070 に答える
1

トリガーでT_RIDER_PONYテーブルを読み取る必要があるのはなぜですか?行を挿入または削除したので、トリガーが行う必要があるのは、T_PONYでポニー名を検索し、テーブルT_RIDERを更新して、このようにTMP_PONYLISTに名前を追加または削除することだけです。

create trigger t_rider_pony_trg 
after insert or delete on t_rider_pony
for each row
begin
   if inserting then
      select pony_name
      into   l_pony_name
      where  pony_id = :new.pony_id;

      update t_rider
      set    tmp_ponylist = tmp_ponylist || ';' || l_pony_name
      where  rider_id = :new.rider_id;
   elsif deleting then
      select pony_name
      into   l_pony_name
      where  pony_id = :old.pony_id;

      update t_rider
      set    tmp_ponylist = ltrim ( 
                               rtrim ( 
                                  replace(';' || tmp_ponylist || ';',
                                          ';' || l_pony_name || ';',
                                          ';'),
                                  ';', ';')
      where  rider_id = :old.rider_id;
   end if;
end;

削除セクションでのその更新は、私が認めるかなり不快なものです。これに対処するには、apex_util.string_to_tableやapex_util.table_to_stringなどのユーティリティを使用することをお勧めします。詳細については、このSOの回答を参照してください。

于 2009-09-22T11:34:32.113 に答える