質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
MySQL

MySQL(マイエスキューエル)は、TCX DataKonsultAB社などが開発するRDBMS(リレーショナルデータベースの管理システム)です。世界で最も人気の高いシステムで、オープンソースで開発されています。MySQLデータベースサーバは、高速性と信頼性があり、Linux、UNIX、Windowsなどの複数のプラットフォームで動作することができます。

SQL

SQL(Structured Query Language)は、リレーショナルデータベース管理システム (RDBMS)のデータベース言語です。大きく分けて、データ定義言語(DDL)、データ操作言語(DML)、データ制御言語(DCL)の3つで構成されており、プログラム上でSQL文を生成して、RDBMSに命令を出し、RDBに必要なデータを格納できます。また、格納したデータを引き出すことも可能です。

MariaDB

MariaDBは、MySQL派生のオープンソースなリレーショナルデータベースシステムです。 また、MySQLとほぼ同じデータベースエンジンに対応しています。

Q&A

解決済

3回答

1571閲覧

MySQL(MariaDB)にてトリガーでINSERT SELECTして他テーブルを効率的に更新する方法

tomona

総合スコア37

MySQL

MySQL(マイエスキューエル)は、TCX DataKonsultAB社などが開発するRDBMS(リレーショナルデータベースの管理システム)です。世界で最も人気の高いシステムで、オープンソースで開発されています。MySQLデータベースサーバは、高速性と信頼性があり、Linux、UNIX、Windowsなどの複数のプラットフォームで動作することができます。

SQL

SQL(Structured Query Language)は、リレーショナルデータベース管理システム (RDBMS)のデータベース言語です。大きく分けて、データ定義言語(DDL)、データ操作言語(DML)、データ制御言語(DCL)の3つで構成されており、プログラム上でSQL文を生成して、RDBMSに命令を出し、RDBに必要なデータを格納できます。また、格納したデータを引き出すことも可能です。

MariaDB

MariaDBは、MySQL派生のオープンソースなリレーショナルデータベースシステムです。 また、MySQLとほぼ同じデータベースエンジンに対応しています。

0グッド

1クリップ

投稿2019/07/27 13:54

###仕様データベース 10.1.26-MariaDB

会計システムを開発しています。
明細テーブルがINSERTされるたびに集計テーブルを更新したく、以下のシナリオでトリガーの設定方法を検証しています。

####<シナリオ>
仕訳テーブルにレコードを追加したときに試算表テーブルに仕訳テーブルのデータをINSERT SELECTして更新するトリガーを設定します。
クラウドのため複数のcompanyが存在しており、それを識別するため仕訳はcompany_id(会社ID)、account_id(科目ID)ごとに集計され試算表テーブルに反映されます。

####<構築したテストテーブル>

-- 仕訳テーブルの作成 CREATE TABLE test_journals ( company_id CHAR(43) NOT NULL, journal_id INT NOT NULL, account_id CHAR(6), amount BIGINT, PRIMARY KEY (company_id, journal_id, account_id) ) DEFAULT CHARSET=utf8; --テストデータ INSERT INTO test_journals VALUES('comd363bb1d6f474b82cc106cfa2303086fa0a671ab','1','AAAAAA','100000'); INSERT INTO test_journals VALUES('comd363bb1d6f474b82cc106cfa2303086fa0a671ab','1','BBBBBB','-100000'); --試算表テーブルの作成 CREATE TABLE test_trial_balances ( company_id CHAR(43) NOT NULL, account_id CHAR(6), total_amount BIGINT, PRIMARY KEY (company_id, account_id) ) DEFAULT CHARSET=utf8;

当初、以下のトリガーを作成し、成功しました。
GroupByをしている場合、ON DUPLICATE KEY UPDATEが使えないようですのでdelete from ~文を入れています。

delimiter // CREATE TRIGGER update_tb_by_journal AFTER INSERT ON test_journals FOR EACH ROW BEGIN delete from test_trial_balances; INSERT INTO test_trial_balances (company_id, account_id, total_amount) (SELECT company_id,account_id, SUM(amount) FROM test_journals GROUP BY company_id, account_id); END ;// delimiter ;

しかし、上記の方法では全データを一度削除し、全データをINSERT SELECTするためデータベースに負荷がかかるのと、あるcompanyの操作で他のすべてのcompany
のデータが操作されてしまう点に懸念があります。

そこで仕訳テーブルにINSERTされたcompany_id及びaccount_idの組み合わせのデータのみINSERT SELECTしたいのですがどのようにすればよいでしょうか。
いろいろ調べてみましたが良案が見つからずアドバイスいただけると幸いです。
また、INSERT SELECT以外にも方法がありましたらご教示いただけますと幸いです。

宜しくお願い致します。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答3

0

トリガーでを使用して効率を的にという事なら、全体の集計ではなく、集計単位のデータに加算する方が良いでしょうね。
当然削除する場合には減算です。

投稿2019/07/27 14:57

sazi

総合スコア25138

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

tomona

2019/07/30 11:35

ご回答ありがとうございます。 例えば集計テーブル(test_trial_balances)のtotal_amountに差分データをといったことでしょうか? 式にするとtotal_amount = total_amount + new.sum(amount) ? まったく新しいレコードがあった場合(total_amountが存在しない場合)もうまくいきますでしょうか。
sazi

2019/07/30 12:40

式としては増減分を加算するだけで、 total_amount = total_amount + new.amount ですね。 更新としてはupsert で insert into ~ ON DUPLICATE KEY UPDATE 構文を使用し、set文を上記の式を指定するようにすれば良いと思います。
tomona

2019/08/05 01:30

追加のご回答誠にありがとうございます。 差分のみ更新する方がパフォーマンス的には向上しそうですね。 トライしてみます。
guest

0

ベストアンサー

毎回トリガで集計するのも非効率的なので、
idを指定して集計するようなプロシージャをつくって
適宜呼び出してやればよいのでは?
(面倒だけど効率をとるか、簡単だけど非効率をとるか)

調整

SQL

1CREATE TABLE test_journals ( 2 company_id CHAR(43) NOT NULL, 3 journal_id INT NOT NULL, 4 account_id CHAR(6), 5 amount BIGINT, 6 PRIMARY KEY (company_id, journal_id, account_id) 7) DEFAULT CHARSET=utf8; 8 9CREATE TABLE test_trial_balances ( 10 company_id CHAR(43) NOT NULL, 11 account_id CHAR(6), 12 total_amount BIGINT, 13 PRIMARY KEY (company_id, account_id) 14) DEFAULT CHARSET=utf8; 15 16/* ここまで提示の通り */

以下ちょっと手抜き(特にidを指定せずに全更新)
プロシージャ作成

SQL

1drop procedure if exists mod_balances; 2delimiter // 3create procedure mod_balances() 4begin 5insert into test_trial_balances 6select company_id,account_id,sum(amount) from test_journals 7group by company_id,account_id 8on duplicate key update total_amount=values(total_amount); 9end 10// 11delimiter ;

test_trial_balancesにダミーデータを入れておく

SQL

1INSERT IGNORE INTO test_trial_balances VALUES 2('X','aaa',150), 3('X','bbb',100);

test_journalsにデータ投入して、その後更新プロシージャを走らせる

SQL

1INSERT IGNORE INTO test_journals VALUES 2('X','1','aaa',100), 3('X','2','aaa',200), 4('X','3','aaa',-150), 5('X','1','bbb',300), 6('X','2','bbb',400), 7('Y','1','aaa',500); 8call mod_balances();

投稿2019/07/29 04:27

編集2019/07/30 12:01
yambejp

総合スコア114574

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

tomona

2019/07/30 11:23

ご回答ありがとうございます。 ストアドプロシージャというものでしょうか。 ググってみたのですが理解が追いつかず、こちらはトリガーとはどのように違うものでしょうか。
yambejp

2019/07/30 12:05

トリガーをわかっているならプロシージャは簡単です。 いつでも呼び出せるトリガーみたいなものだと思えばいいでしょう 任意の値を引数で渡すこともできますが今回はパス サンプルを確認してみてください
tomona

2019/08/05 01:28

ありがとうございます。 プロシージャ理解しました。 この方法でうまくいきそうです! なお、test_trial_balancesにダミーデータを入れておりますが、こちらは入れなくても動くようです。 どのような理由で入れているか最後に伺えますでしょうか。 お手数ですが宜しくお願い致します。
yambejp

2019/08/05 02:05

ダミーをいれたのは、挙動を確認するためです。
tomona

2019/08/15 07:38

ありがとうございます。 大変参考になりました。
guest

0

直接の回答ではありませんが、

test_journals って例えば1時間辺りに何件INSERTされますか? test_journalsを1レードINSERTする度に test_trial_balances を全件DELETEしてINSERT ... SELECT するのって、コストが多すぎませんか?
質問にはありませんが、test_journals のUPDATE, DELETE のトリガーもありそうな気もします。

トリガーもtest_trial_balances も使わずに、test_trial_balances の内容が必要になったところで、

SQL

1SELECT company_id,account_id, SUM(amount) 2FROM test_journals 3GROUP BY company_id, account_id ;

を実行するのとでパフォーマンスの違いを調べてみては?

投稿2019/07/27 22:04

Orlofsky

総合スコア16415

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

tomona

2019/07/30 11:31

ご回答ありがとうございます。 test_journalsは1時間当たり10,000件程度INSERTされると想定します。 >> test_journalsを1レードINSERTする度に test_trial_balances を全件DELETEしてINSERT ... SELECT するのって、コストが多すぎませんか? はい、したがって、効率よく更新する方法がないかというのが今回のご質問の趣旨となります。 ご指摘の通り、UPDATE、DELETEもトリガーとなりえます。 test_trial_balancesの数値はその後の帳票の計算でも用いられます。その先の帳票を呼び出すたびに仕訳レコード(test_journals)からさかのぼって集計する方法は抽出ロジックが複雑になるため難しいと考えています。
Orlofsky

2019/08/05 02:01

SELECT company_id,account_id, SUM(amount) FROM test_journals GROUP BY company_id, account_id ; よりどれだけ複雑になるのかの情報がないと適切なコメントは付きにくいかと。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問