特定のカラム更新時のトリガ作成方法

このQ&Aのポイント
  • 特定のカラムが更新されたときのみ、そのレコードを別テーブルへ格納するトリガの作成方法を教えてください。
  • テーブルMV_ORACLE_MV_TESTのうち、CHANGENAMEカラムが変更されたレコードのみを、TRITEST_ORACLE_MV_TESTテーブルに格納したいと思っています。他のカラムも一緒に格納する必要があります。
  • 上記のトリガを作成しましたが、コンパイルエラーが発生しました。どの部分が誤っているか教えてください。
回答を見る
  • ベストアンサー

特定のカラムが更新されたときのみ、そのレコードを別テーブルへ格納するト

特定のカラムが更新されたときのみ、そのレコードを別テーブルへ格納するトリガの作成方法を教えてください・ トリガ名:TEST_UPDATE テーブル名:MV_ORACLE_MV_TEST カラム名:ORAPRIME ORACHANGE ORANAME CHANGENAME 対象カラム:CHANGENAME 格納先テーブル:TRITEST_ORACLE_MV_TEST 作成トリガ: CREATE OR REPLACE TRIGGER TEST_UPDATE after update of CHANGENAME on MV_ORACLE_MV_TEST FOR EACH ROW begin INSERT INTO TRITEST_ORACLE_MV_TEST SELECT MV_ORACLE_MV_TEST.ORAPRIME , MV_ORACLE_MV_TEST.ORACHANGe , MV_ORACLE_MV_TEST.ORANAME , MV_ORACLE_MV_TEST.CHANGENAME FROM MV_ORACLE_MV_TEST where MV_ORACLE_MV_TEST.CHANGENAME = :new.CHANGENAME -- commit; commit; end / テーブルMV_ORACLE_MV_TESTのうち、CHANGENAMEカラムが変更(更新)されたレコードのみを、 TRITEST_ORACLE_MV_TESTテーブルに格納したいと思っております。 この時、他のカラムも一緒に格納することが必要でして、 (もしカラムに変更があった場合、そのカラムも変更する。変更されていない場合は元の値を格納する) そのトリガを上記のように書いたのですが、 コンパイルエラーが発生しました。 どの部分が誤っているのか、ご教授願えませんでしょうか

  • Oracle
  • 回答数7
  • ありがとう数9

質問者が選んだベストアンサー

  • ベストアンサー
  • yamada_g
  • ベストアンサー率68% (258/374)
回答No.7

・・・ご自身で少しは考えていらっしゃいますか? やりたいことはそういうことなのだろうと思っていましたが敢えて書きませんでした。 CHANGENAMEが変更されたら、というIF文を書けばいいだけなのではないですか? if :old.CHANGENAME != :new.CHANGENAME THEN --insert end if; CHANGENAMEにnullが入る場合は上記の条件では足りないと思いますが、 そこはご自身で書いてください。

amatsuno
質問者

お礼

ありがとうございました。 if文は最初考えていたのですが、 指定する場所を間違えていたために失敗して諦めていました。 nullに関しては、テーブル側でnot null 制約のあるカラムに指定することによって対処しました

その他の回答 (6)

  • yamada_g
  • ベストアンサー率68% (258/374)
回答No.6

No.1、No.4です。 なぜそうしようと思うのかが自分にはよく分かりません。 MV_ORACLE_MV_TESTは参照できないということも言っているのですが・・・ No.2さんが >selectするんじゃなく、:new.xxxxxをinsertの値にしてください。 と回答してくれていますよね? 素直に insert into TRITEST_ORACLE_MV_TEST values ( :new.ORAPRIME , :new.ORACHANGe , :new.ORANAME , :new.CHANGENAME ) とすればいいのでは? マニュアルは読みましたか?

amatsuno
質問者

お礼

ありがとうございます。 上記の形で先程実行したのですが、 その際、マテリアライズド・ビューへの更新が複数発生したときに CHANGENAMEを変更していないレコードも抜き出してしまいましたため、 selectで別途絞ることが必要なのかと思ってselectが必要なのかと書きました。 言葉足らずで申し訳ございません。 下記の条件で不要なものが出てしまいました。 ・マテリアライズド・ビューの更新の時、CHANGENAMEを変更したものとCHANGENAME以外のカラムを変更したものが混在して変更される ・別テーブルへの書き込みがマテリアライズド・ビューへのCHANGENAMEカラムの変更があった場合をトリガとしている ・そのため、トリガの起動条件(CHANGENAMEカラムの変更があるレコードが発生)を満たしているときに、CHANGENAMEカラム以外の変更がされたレコードがあった場合、そのレコードも一緒に書き出されてしまう と言う事象です。 ※ (1)MVIEWの更新で200件更新が実行される (2)別テーブル抜き出しの対象となる、CHANGENAMEカラムが変更されたものはそのうちの40件 (3)MVIEWでCHANGENAMEカラムに変更があることをトリガとし、トリガはその時に更新されたすべてのレコードを別テーブルに抜き出しているため、別テーブルへは200件が抜き出されてしまう →目的は、このようなケースで(2)に該当する40件を抜き出したい

  • SaKaKashi
  • ベストアンサー率24% (755/3136)
回答No.5

INSERTする値が、UPDATEトリガーの対象の表なのですからSELECTで値を求める必要はないと思いますけどね。 全て、:new.項目名でいいんじゃないですか。 CREATE OR REPLACE TRIGGER Log_salary_increase AFTER UPDATE ON Emp_tab FOR EACH ROW BEGIN INSERT INTO Emp_log (Emp_id, Log_date, New_salary, Action) VALUES (:new.Empno, SYSDATE, :new.SAL, 'NEW SAL'); END;

amatsuno
質問者

お礼

ありがとうございます。 ただ、上記の方法ですと、 (1)MVIEWの更新で200件更新が実行される (2)別テーブル抜き出しの対象となる、CHANGENAMEカラムが変更されたものはそのうちの40件 (3)MVIEWでCHANGENAMEカラムに変更があることをトリガとし、トリガはその時に更新されたすべてのレコードを別テーブルに抜き出しているため、別テーブルへは200件が抜き出されてしまう という事象が発生してしまったので、どこかでselect条件が必要なのかなと考え、 select文を条件指定に入れていました。 今回、MVIEWに反映されたもののうち、update of 句で指定したカラムが変更されたレコードのみを抽出したいので、その時に同時に出てくる別レコード(update of句以外の箇所が変更されたレコード)を抽出しないという条件がネックになっています。

  • yamada_g
  • ベストアンサー率68% (258/374)
回答No.4

No.1です。 >本件は元テーブル側に原因がありました。 とありますが、コンパイル自体が出来ないのはセミコロンが抜けているからなのではないですか? 実際は記載されたコードと違ったということなのでしょうか。 >トリガのupdate条件は、 >マテリアライズド・ビューの更新を見ることはできないのでしょうか? 高速リフレッシュならばupdate文が実行されるはずですので検知できると思います。 完全リフレッシュだとdelete(もしくはtruncate)→insertとなるはずなので、検知できないのではないでしょうか。 また、 >もしかして、トリガの場合、insert into ~ select ~ >のテーブルコピー文法は使用できないのですか? トリガーでは変更表(今回であればMV_ORACLE_MV_TEST)を参照することはできません。 ですので、No.2さんのご指摘にあるようにINSERT文を修正してください。 http://otndnld.oracle.co.jp/document/products/oracle10g/102/doc_cd/appdev.102/B19248-02/adfns_triggers.htm

amatsuno
質問者

お礼

セミコロンについては申し訳ございません。 このページに貼るときにコピーが漏れていました (where句の最終位置のセミコロンが抜けていました) MVIEWのリフレッシュは高速リフレッシュになっています また、非常に申し訳ないのですが、 insert文の変更イメージが良く沸かないのですが、 insert into TRITEST_ORACLE_MV_TEST values ( aaa, bbb, ccc, ddd ) で書くときに、予めそれぞれの値を文頭でdeclareすることが必要ということでしょうか? つまり、 declare aaa VARCHAR2(45) select ORAPRIME into ORAPRIME from MV_ORACLE_MV_TEST where MV_ORACLE_MV_TEST.CHANGENAME = :new.CHANGENAME; bbb ~~~(以下、aaaに同じ) をbeginの前に追加する方法と言う意味です

  • SaKaKashi
  • ベストアンサー率24% (755/3136)
回答No.3

コンパイルエラーの内容がわかりませんので、これ以上のコメントは無理かと。 マニュアルの一部ですが、 トリガー本体で使用可能なSQL文 トリガー本体には、DML SQL 文を含めることができます。 また、SELECT 文を含めることはで きますが、SELECT... INTO... 文またはカーソル定義内のSELECT 文を指定する必要があります。

amatsuno
質問者

お礼

すいません。 コンパイルエラーについては、元テーブル(MVIEW)の命名エラーでしあt。 ご迷惑をおかけいたしました。 >SELECT... INTO... 文またはカーソル定義内のSELECT 文を指定する必要があります。 というのは、declareが必要ということでしょうか? あるいは、下記のような書き方なのでしょうか? INSERT INTO TRITEST_ORACLE_MV_TEST SELECT (      select ORAPRIME into oraprime from MV_ORACLE_MV_TEST, select ORACHANGE into ORACHANGE from MV_ORACLE_MV_TEST , select ORANAME into ORANAME from MV_ORACLE_MV_TEST, select CHANGENAME into CHANGENAME from MV_ORACLE_MV_TEST ) FROM MV_ORACLE_MV_TEST where MV_ORACLE_MV_TEST.CHANGENAME = :new.CHANGENAME;

  • SaKaKashi
  • ベストアンサー率24% (755/3136)
回答No.2

このタイプのトリガー内にはcommitは書けません。 selectするんじゃなく、:new.xxxxxをinsertの値にしてください。

amatsuno
質問者

お礼

ありがとうございます。 commitは作成途中の残骸でした。 (こちらを削除しました) もしかして、トリガの場合、insert into ~ select ~ のテーブルコピー文法は使用できないのですか?

  • yamada_g
  • ベストアンサー率68% (258/374)
回答No.1

>コンパイルエラーが発生しました。 エラーメッセージは読みましたか?どの行でエラーになっているかも出力されていると思います。 読んでも分からないのであれば、少しずつコードを追加していく等をして、 どこでエラーになっているのかを自分で見つけてください。 エラーの原因はただの文法ミスです。 余計なお世話ですが、これくらいは自分で見つけられるようにならないとこれからも大変だと思いますよ。

amatsuno
質問者

補足

すいません。 本件は元テーブル側に原因がありました。 お騒がせいたしました。 また、もう一つご教授願いたいのですが、 トリガのupdate条件は、 マテリアライズド・ビューの更新を見ることはできないのでしょうか? 本文を引用しますと、 after update of CHANGENAME on MV_ORACLE_MV_TEST の「MV_ORACLE_MV_TEST」 がマテリアライズド・ビューであり、 元テーブルの変更→マテリアライズド・ビューの更新の発生した際に、 マテリアライズド・ビュー内の当該レコードを別テーブルに保管しようとしたのですが、 更新ができませんでした

関連するQ&A

  • レコード内容からテーブル特定

    Oracleのデータ更新について質問させてください。 SQL文で、ある文字列を含むレコードデータを持つ、テーブルおよびレコード名を知る方法はありますでしょうか? 具体的には、DBに登録されている"google.co.jp"という文字列を"yahoo.co.jp"という文字列に全てupdateしたいのですが、 "google.co.jp"という文字列は複数のテーブルの不特定のレコードに格納されています。 全てのテーブルを1つ1つselectで見ていくしか方法は無いのでしょうか?

  • 日付をカラム格納せずテーブルとして作成しても良い?

    SQLite3なんですが、日付データをカラムへ格納せず、日付毎にテーブル作成してデータを格納しているコードがあるのですが、これは一般的なやり方なのでしょうか? ・一般的なやり方 ・一般的なやり方ではない ・一般的なやり方ではないが、アプリ内容(仕様)によっては普通に起こりうる ■質問背景 ・日付はカラムへ格納するものとばかり思っていました ・ちょっとびっくりしたので、質問してみました

  • トリガからプロシージャのコールについて

    データベースはOracle10gです。 あるテーブルAにレコードが登録されたタイミングで、 別のテーブルBにレコードを登録するトリガを作成しようとしています。 トリガ内に全ての処理を記述するとかなりの行数になりそうなので、 登録のプロシージャを作成し、トリガからコールしようと考えています。 Aに登録されたレコードのカラムの一部を、Bに登録するレコードでも使用したいのですが、 プロシージャに引数として渡す際、 使用したいカラムを一つずつ指定しなければならないのでしょうか? (使用したいカラムは10ほどあります。) トリガに 「REFERENCING NEW ROW R1」と記述して、 このR1を渡すことはできないのでしょうか?

  • テーブルで一番古いレコードだけをSELECTしたい

    テーブルで一番古いレコードだけをSELECTしたいのですが、どうすれば良いでしょうか? ■背景 ・テーブルからデータを取得しようと思ったら、「id」及び「autoincrement」に該当するカラムがありませんでした ・日付に該当するカラムもありません ・「phpMyAdmin」で確認すると、いつも同じ並び順で表示されるので、格納したレコード順で表示されているのではないかと思いました ■質問 ・この時、そのテーブルで一番古いレコードだけをSELECTしたいのですが、どうすれば良いでしょうか? ・where?

    • ベストアンサー
    • MySQL
  • 同テーブルで指定カラム値を相互更新(入替)したい

    下記のようなカラムがある時、DBから取得した値を、引数(並べ替え順)に応じて、(同じカラムを)更新するSQL文を教えてください ●カラム id1 sex1 age1 id2 sex2 age2 id3 sex3 age3 id4 sex4 age4  ●引数例 3 1 2 ※1番目が3、2番目が1、3番目が2 ・「1の数字が付いたカラムの値」を、「3の数字が付いたカラムの値」で更新したい ・「2の数字が付いたカラムの値」を、「1の数字が付いたカラムの値」で更新したい ●質問 ・MySQLだけで処理可能でしょうか? UPDATE テーブル名 SET id1=(SELECT id3 FROM テーブル名 WHERE id1=id3値),sex2=(SELECT sex3 ……; ・それとも、一度、全ての値を取得して、PHP等で処理するのでしょうか? UPDATE テーブル名 SET id1=id3値,sex1=sex3値,age1=age3値,id2=id1値 ……; ・他に何か良い方法はあるでしょうか(一旦別テーブル作成後、戻す等) ●環境 MySQL5.1

    • ベストアンサー
    • MySQL
  • csvファイルを使ってMySQLのテーブルを更新し

    約2万件ある15個のカラムで構成されるMySQLのテーブルがあります。 このテーブルをcsvファイルを使って更新しようと考えています。 csvファイルにはレコードIDとあるカラムの変更する値の2つのセルで 構成されています。 行数は毎回異なりますが、だいたい300行前後です。 csvファイルの具体的イメージはこんな感じです。 id order ============ 2,  200 4,  10 7,  460 11,  35 MySQLのテーブルの方にも「id」と「order」というカラムが存在します。 java や PHP を使ってやる方法はわかるんですが、プログラムを作成しないで SQLだけで行いたいと考えています。 LOAD DATA LOCAL INFILE などがあることがわかったのですが、レコードを REPLACE  するのではなく、一部のカラムをアップデートしたい、ということです。 どなたか、教えて頂けると助かります。 よろしくお願い致します。

  • コラム名でテーブルを検索できますか?

    SQL & ORACLE初心者です。最近、セレクト文を使って、テーブル内のデータを取り出すことができるようになりましたが、逆にコラム名からテーブルを検索することはできるでしょうか。 例えば、TABLE_A内のコラムの中にCOLUMN_Aという名前のコラムがあるとして、このコラムが他のテーブルでも使われているかどうか、いるとしたらどのテーブルかを知りたいのです。また、COLUMN_Aと別のCOLUMN_B(それぞれが単独で記載されているテーブル名はわかっているとします)が一緒にある一つのテーブルで使われているかどうか、またそのテーブル名を知りたいときには、どのようなSQLを書けばよいでしょうか。 DBAやその他DBを構築するための知識をお持ちの方、ご回答をお待ちしています。

  • テーブル更新時にお知らせ

    いつもお世話になります。 早速質問させていただきます。 1日に2、3回更新されるあるテーブルがあるのですが、そのテーブルが書き換わった(insert,update) タイミングで音を鳴らすなどして、管理者にお知らせしたいのです。 只今pl/sqlとOracleのトリガの組み合わせで只今試行錯誤している状況です。 この組み合わせで実現できますでしょうか? 既にPGMで実現できたのですが、常駐監視部分が気に入らなく、何とかOracleの機能で 実現できないかと考えています。 宜しくお願いします。 サーバ環境: Oracle8i Windows2000Server

  • 他テーブルの集計結果を別テーブルのカラムに格納

    新規テーブル作成時にあるカラムに別のテーブルの集計結果を入れることはできるのでしょうか? 例えば、商品を定義したテーブルの中に、その商品が合計いくつ売れているかを集計するカラムを設置し、常に発注テーブルからその商品の合計発注数を引っ張って来て格納するイメージです。 テーブル作成時のみ集計するではなく、常に参照し動きがあれば数値が自動で変わるようになる方法を探しています。 いちいちselectで集計しなくても始めからカラムに集計値が入っていれば楽だなぁと思い質問しました。 どなたかわかる方いらっしゃいましたらよろしくお願いします。 商品テーブル CREATE TABLE product ( product_no SERIAL NOT NULL, (商品id) product_name VARCHAR(200) NOT NULL, (商品名) product_price VARCHAR(20) NOT NULL, (料金) product_sum INTEGER, (合計発注数)←ここ PRIMARY KEY(product_no) ); 発注テーブル CREATE TABLE order ( order_no SERIAL NOT NULL, (発注id) product_no INTEGER NOT NULL REFERENCES product(product_no) ON UPDATE CASCADE ON DELETE CASCADE,(商品id) order_num INTEGER NOT NULL,(その商品の発注数) order_date TIMESTAMP,(発注日) PRIMARY KEY(order_no) );

  • MySQL+PHP 特定レコードの更新と作成

    MySQL+PHPの初心者です。 特定レコードの変更または新規作成のサンプルなどあればお教えいただけるでしょうか。 例えば特定商品の単価のレコードがあれば新しい単価に変更と そのレコードが無ければ新規にレコードを作成するようなケースになります。 VBAなどでは検索のSQL文からレコードセットを作成し レコードが無い場合は レコードセット名.addnewで必要なフィールドに 値をセットし レコードセット名.update 該当するレコードがあった場合は レコードセット名.edit で必要なフィールドに 値をセットし レコードセット名.updateなど簡単な処理なのですが MySQL+PHP の場合データの検索と新規レコードの単純な記述は ある程度はわかるのですが上記のようなケースは実際どのように 書かれているのかお教え願えると幸いです。 商品単価テーブルには商品コードと単価のフィールドになります。 よろしくお願いいたします。

    • ベストアンサー
    • MySQL