🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
MySQL

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

SQL

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

トランザクション

トランザクションとは、関連・依存する処理を一連の不可分な処理単位として扱う処理方式を指します。トランザクションとして管理された処理は「すべて成功」か「すべて失敗」のいずれかであることが保証される。処理に失敗した場合は、一連の処理がロールバックされます。

Q&A

2回答

2335閲覧

MySQLのトランザクションで最後のSQLしか実行されない

yoyoyo_yoyoyo

総合スコア5

MySQL

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

SQL

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

トランザクション

トランザクションとは、関連・依存する処理を一連の不可分な処理単位として扱う処理方式を指します。トランザクションとして管理された処理は「すべて成功」か「すべて失敗」のいずれかであることが保証される。処理に失敗した場合は、一連の処理がロールバックされます。

0グッド

0クリップ

投稿2019/10/25 03:00

編集2019/10/25 04:12

前提・実現したいこと

MySQLでトランザクション内で複数SQLを実行してコミットすると最後のSQLしか有効になりません。
原因等わかる方いらっしゃいますか?

発生している問題

使用しているMySQLのバージョンは以下のとおりです。

# mysql --version mysql Ver 15.1 Distrib 10.4.6-MariaDB, for Linux (x86_64) using readline 5.1

データベースには以下の2テーブルがあります。

test1 +-------+---------------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +-------+---------------------+------+-----+---------+----------------+ | id | bigint(20) unsigned | NO | PRI | NULL | auto_increment | | name | varchar(255) | YES | | NULL | | +-------+---------------------+------+-----+---------+----------------+ test2 +----------+---------------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +----------+---------------------+------+-----+---------+----------------+ | id | bigint(20) unsigned | NO | PRI | NULL | auto_increment | | test1_id | bigint(20) unsigned | NO | | NULL | | | name | varchar(255) | YES | | NULL | | +----------+---------------------+------+-----+---------+----------------+

このテーブルに以下のSQLを実行しました。
(テーブルには何もデータは入っていません)

begin; insert into test1 (id, name) values (null, 'test'); insert into test2 (id, test1_id, name) values (null, 1, 'test'); commit;

SQL実行後のテーブルのデータは以下のとおりです。

select * from test1; Empty set (0.001 sec) select * from test2; +----+----------+------+ | id | test1_id | name | +----+----------+------+ | 1 | 1 | test | +----+----------+------+ 1 row in set (0.001 sec)

2つ目のSQLしか実行されていないようです。

トランザクションをかけずに個別にinsertを実行すると問題なく登録できています。

コミットではなく、ロールバックをするとtest1、test2どちらにもデータが入っていない状態となります。(正しい挙動)

MySQLの設定等原因わかる方いらっしゃいますでしょうか?
よろしくお願いします。

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

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

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

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

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

guest

回答2

0

普通の設定ではtest1の投入が失敗したらtest2には処理がながれないと思いますが・・・

SQL

1create table test1( 2 id bigint unsigned not null primary key auto_increment, 3 name varchar(255) 4 ) ENGINE=InnoDB; 5create table test2( 6 id bigint unsigned not null primary key auto_increment, 7 test1_id bigint unsigned not null default 0, 8 name varchar(255) 9 ) ENGINE=InnoDB;
  • 失敗するSQL

SQL

1start transaction; 2insert into test1 (id, name) values (1, 'test1'); 3insert into test1 (id, name) values (1, 'test2'); 4insert into test2 (id, test1_id, name) values (null, 1, 'test3'); 5commit;

オートコミットで、エラーを無視するような設定にでもなっているのでしょうか?

参考

にあるとおり、トランザクションの原子性から考えて、
「全体を適用するか、またはロールバックするかのどちらかである」と提示されています。
「一部が完了することはあり得ない。全か無かのどちらか一方しかない」
のがトランザクションです。

どうしても直らないならmysql自体の再インストールが必要かもしれません

投稿2019/10/25 03:38

編集2019/10/25 05:32
yambejp

総合スコア116694

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

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

yoyoyo_yoyoyo

2019/10/25 04:21

ご回答ありがとうございます。 トランザクションをかけずに個別にSQLを実行するとtest1にもデータは登録されるのでエラーにはなっていないと思います。
yambejp

2019/10/25 04:24

SQLの発行はなにをつかってやっています? ・mysqlクライアントにログインして手動で発行 ・phpMyAdminなどなんらかのUI ・なんらかのプログラム
yambejp

2019/10/25 04:38

それと仮に insert into test1 (id, name) values (1, 'test'); というidを直接していしてもダメですか?
yoyoyo_yoyoyo

2019/10/25 04:46

> SQLの発行はなにをつかってやっています? SSHでサーバに接続し、mysqlクライアントにログインして手動で発行しています。 idを直接指定してもダメでした。
yambejp

2019/10/25 04:51

私の環境でmysqlクライアントにログインしてやったところ 再現性はないですね 逆にcommitではなくrollbackした時に、test2への投入は ちゃんとキャンセルされますか?
yoyoyo_yoyoyo

2019/10/25 05:00

rollbackはtest1、test2どちらにもデータは入らず正常に動いています。
yambejp

2019/10/25 05:06 編集

あとtest1とtest2の投入順番を逆にするとどうですか? 今度はtest1だけが投入されます? またtest1の投入を2度する(test2はしない)場合、投入されるのは 1つだけでしょうか? begin; insert into test1 values(1,'test1'); insert into test1 values(2,'test2'); commit; またバルクインサートはどうでしょうか? begin; insert into test1 values(1,'test1'),(2,'test2'); commit;
yoyoyo_yoyoyo

2019/10/25 05:15

> あとtest1とtest2の投入順番を逆にするとどうですか? test2にはデータが入らずtest1にだけデータが入りました。 > またtest1の投入を2度する(test2はしない)場合、投入されるのは 1つだけでしょうか? 後に実行した1件のみデータが登録されます。(test2の方) > またバルクインサートはどうでしょうか? こちらでは2件データが登録されました。
yambejp

2019/10/25 05:34

動作の切り分けはできてきたみたいですがそれにしてもおかしいですね。 (追記した参考のとおり) tableのエンジンは間違いなくinnoDBですよね? ダメならあきらめて再インストールするか トランザクションに依存しない処理で我慢するかでしょう
yoyoyo_yoyoyo

2019/10/25 06:29

テーブルのエンジンはinnoDBです。 トランザクションは必須なので再インストールしようと思います。
yambejp

2019/10/25 06:33

test1とtest2の依存関係がわからないのでなんともいえませんが test1の投入によりtest2にデータが投入されるなら 普通はtest1のトリガーでやる処理のような気がします。 もちろんtest1のトリガーが発火しなければtest2にデータが登録されない ので、状況的にかわらないかもしれませんけどね
guest

0

id は NOT NULL 制約が設定してある(NULL禁止)からだからでは?
auto_increment が設定してあるので、

SQL

1insert into test1 (id, name) values (null, 'test'); 23insert into test1 (name) values ('test');

で試しては?

投稿2019/10/25 03:07

Orlofsky

総合スコア16417

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

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

yoyoyo_yoyoyo

2019/10/25 04:06

ご回答ありがとうございます。 上記のようにidを指定しない方法を試してみましたが結果は変わりませんでした。
Orlofsky

2019/10/25 04:27

https://dev.mysql.com/doc/refman/5.6/ja/constraint-invalid-data.html >STRICT_TRANS_TABLES は、トランザクションストレージエンジンに対して厳密モードを有効にし、非トランザクションエンジンに対してもある程度有効にします。 を試しては? MySQLはあえて厳密にチェックしないことでパフォーマンスを稼いでいる部分があります。
yoyoyo_yoyoyo

2019/10/25 05:18

デフォルトでSTRICT_TRANS_TABLESは有効になっていました。 無効にして試してみましたが結果は変わりありませんでした。
Orlofsky

2019/10/25 05:29

あえて地雷原の中を進むのは避けたいですから、 insert into test2 (id, test1_id, name) values (null, 1, 'test'); も insert into test2 (test1_id, name) values (1, 'test'); に。 動作確認の為に別名のテーブルを作って試しては?
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

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

ただいまの回答率
85.36%

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

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

質問する

関連した質問