アーカイブ「2026年01月」

magentadesk の 0.9.0 をリリースしました。

https://github.com/shigenobu/magentadesk/releases

MySQL 8.4の

ERROR 1030 (HY000): Got error 124 - 'Wrong index given to function' from storage engine

のエラーに対応すべく、差分検出クエリーの書き方を大きく変更しました。

この問題は、非常にやっかいでしたが、

書き方変更により、MySQL 8.0系で発生していた重複計上も解消したので、

多少のパフォーマンス向上も見られる形となっています。

この話はUNIONに関係する問題で、色々検証したので、違う記事に詳細を記載しておきます。

さて、magentadeskですが、同じ筐体(同じプロセスが管理する)にある2つのデータベースのテーブルについて、

差分を詳細に検出して、同期するものなのですが、

リモートとの比較をうまくできないかを思案中です。

今のロジックでは、完全外部結合を行い、その差分を利用しているのですが、

リモートとなると、透過的エンジン(Spiderなど)を使って実現したとて、

パフォーマンスが大きく落ちてしまいます。

そのため、レプリケーションをキャプチャして、差分用のテーブルを作っておくなどの対応が必要となります。

ちょっと難しそうですが、やってみようかなとは検討してます。

以上

投稿日時:2026年01月30日 17:12   カテゴリー:java, mariadb, mysql   [コメントがあればどうぞ]

magentadesk 0.9.0 のリリースでも触れましたが、

MySQLのUNIONに非常に苦しめられました。

具体的には、完全外部結合をする過程の中で、使っているUNIONの箇所で、

ERROR 1030 (HY000): Got error 124 - 'Wrong index given to function' from storage engine

というエラーが発生します。

この原因がどうもMySQL 8.0 から導入されたTempTableエンジンという一時テーブル用のエンジンの不具合のようです。。


そもそも完全外部結合で何をしたいかというと、以下のようなイメージとなります。

要は、同じ構成の2テーブルのレコード差を出す形です。

以下のテーブルとデータを用意してます。

create table base (
  id int not null primary key,
  name text not null
);
insert into base values (1, 'A'),(2, 'B'),(3, 'C');

create table compare (
  id int not null primary key,
  name text not null
);
insert into compare values (1, 'A'),(2, 'BB'),(4, 'D');

外部結合がサポートされているPostgreSQLだと簡単にできます。

select
  base.id as base_id, 
  base.name as base_name, 
  compare.id as compare_id, 
  compare.name as compare_name 
from
  base full outer join compare on base.id = compare.id
where
  base.id is null or compare.id is null
  or
  base.id != compare.id or base.name != compare.name
;

 base_id | base_name | compare_id | compare_name 
---------+-----------+------------+-------------
       2 | B         |          2 | BB
       3 | C         |       NULL | NULL
    NULL | NULL      |          4 | D

magentadesk での以前の実装は以下のとおりでした。

これはMariaDBでは問題ありませんでした。当然PostgreSQLでも問題なかったです。

ただし、MySQL 8.0系だと、正しくない結果でした。(結果的にはwhere句を追加して対応)

with
only_base as (
  select * from base
  except
  select * from compare
),
only_compare as (
  select * from compare
  except
  select * from base
),
merge_left as (
  select 
    only_base.id as base_id, 
    only_base.name as base_name, 
    only_compare.id as compare_id, 
    only_compare.name as compare_name
  from only_base left outer join only_compare on only_base.id = only_compare.id
),
merge_right as (
  select 
    only_base.id as base_id, 
    only_base.name as base_name, 
    only_compare.id as compare_id, 
    only_compare.name as compare_name 
  from only_base right outer join only_compare on only_base.id = only_compare.id
),
merge_full as (
  select * from merge_left
  union
  select * from merge_right
)
select
  *
from
  merge_full
;

(MariaDB 12.1)
+---------+-----------+------------+-------------+
| base_id | base_name | compare_id | compare_name |
+---------+-----------+------------+-------------+
|       2 | B         |          2 | BB          |
|       3 | C         |       NULL | NULL        |
|    NULL | NULL      |          4 | D           |
+---------+-----------+------------+-------------+

(PostgreSQL 18.1)
 base_id | base_name | compare_id | compare_name 
---------+-----------+------------+-------------
       3 | C         |       NULL | NULL
       2 | B         |          2 | BB
    NULL | NULL      |          4 | D

(MySQL 8.0.44)
+---------+-----------+------------+-------------+
| base_id | base_name | compare_id | compare_name |
+---------+-----------+------------+-------------+
|       2 | B         |          2 | BB          |
|       3 | C         |       NULL | NULL        |
|       1 | A         |          1 | A           |
|    NULL | NULL      |          4 | D           |
+---------+-----------+------------+-------------+

このクエリーはMySQL 8.4 だと動きません。

そのため、次の形に変更しました。

with
only_base as (
  select id, MD5(IFNULL(name, '')) as row_hash from base
  except
  select id, MD5(IFNULL(name, '')) as row_hash from compare
),
only_compare as (
  select id, MD5(IFNULL(name, '')) as row_hash from compare
  except
  select id, MD5(IFNULL(name, '')) as row_hash from base
),
diff_keys as (
  select id from only_base
  union
  select id from only_compare
),
merge_full as (
  select
    base.id as base_id, 
    base.name as base_name, 
    compare.id as compare_id, 
    compare.name as compare_name 
  from
    diff_keys
    left outer join base on diff_keys.id = base.id
    left outer join compare on diff_keys.id = compare.id
)
select
  *
from
  merge_full
;

ポイントは、

  • EXCEPTの比較時はハッシュ化しておく
  • UNIONでは主キーのみとする
  • 最後の外部結合で情報を取得する

といったところで、ハッシュ化する負荷はあるもののトータルで見れば軽くなった形となります。

これで、MariaDBも、MySQL 8.0および8.4 でも同じ結果を得ることができました。

さすがにわからかなったので、Geminiにも聞きながらやりました。

とはいえ、結構Geminiも間違えていたので、なかなか厄介な問題なのかなと思っています。


magentadesk での以前の実装のクエリーの問題点は、

merge_full as (
  select * from merge_left
  union
  select * from merge_right
)

の部分なのですが、文字列カラム(char/varchar/text)があると、エラーを引き起こすようです。

バイナリは試していないのですが、同じかもしれません。

もちろん、通常のテーブルのUNIONは問題ないのですが、仮想表でのUNIONには気をつけたほうがいいのかな、という所感です。

UNIONだけなの問題なのか、EXCEPTやINTERSECTまで及ぶのかはわからないのですが、

TempTableにはまだ不具合ありそうなので、テンポラリーに落ちるとき(Using temporary)はご注意ください。

以上

投稿日時:2026年01月30日 17:12   カテゴリー:mariadb, mysql, postgresql   [コメントがあればどうぞ]

ebonyrack 1.0.1 をリリースしました。

https://github.com/shigenobu/ebonyrack/releases

今回は無事?1.0.0を超えたので、ロゴを修正しつつ、

カラムへのエイリアス設定という機能を追加しました。

これはなんてことない機能なのですが、

クラスファイルを出力時に利用されることを想定しています。

たとえば、データベース上では、intで定義した型であっても、

プログラムではENUMで扱いたいよね、などというときに利用するものです。

エイリアスは3つまで設定できるので、3つのプログラム言語まで対応ってイメージにしてます。

また、カラムの説明なんかも、ノートを使うと見づらくなってしまう場合もあるので、

ここに説明をかくと、DDLにコメントとして表示されるようにしています。

総じて、クラスファイル出力時のお助け機能といったところです。

がんばって体裁は整えていますが、ソースコードもかなり量が多く、

メンテナンスコストもかかるようになってきました。

やっぱり、もうちょっと分割できるような仕組みがよいなーと思い、最近はavalonia.uiを勉強中です。

WPFを知っていることが前提なので、しんどい部分もありますが、

今後記事にできればとは考えています。

以上

投稿日時:2026年01月30日 14:26   カテゴリー:java, mariadb   [コメントがあればどうぞ]