VillageSQL is a drop-in replacement for MySQL with extensions.
All examples on this page work on VillageSQL. Install Now →
どのアプローチを使うべきか
- 一回限りの切り替え —
pg_dumpとLOAD DATA INFILEによるダンプとリストア。後述の移行ツールと移行の手順を参照してください。 - 移行中も移行元データベースを稼働させ書き込み可能にしておく必要がある、または継続的な同期が必要 — 一回限りのダンプの代わりに、JDBC シンクコネクタを使った Debezium で継続的なレプリケーションを設定します。
移行ツール
pg_dump と手作業でのスキーマ変換が主要な道であり、標準の PostgreSQL と MySQL のクライアントツール以外は何も必要としません。
pg_dump のスキーマ出力は PostgreSQL の構文 (SERIAL、BOOLEAN、PostgreSQL 固有の CREATE TABLE 句) を使うため、MySQL に対して実行する前に、後述の型の対応表を使ってスキーマファイルを手作業で変換する必要があります。この変換をあらゆるケースで自動的かつ正しく行うツールはありません。これをほとんどの移行のデフォルトの道として扱ってください。
JDBC シンクコネクタを使った Debezium (debezium.io、Apache-2.0 ライセンス、活発に保守されている) は、PostgreSQL の先行書き込みログを読み、Kafka Connect を通じて変更を MySQL にストリーミングし、一回限りのコピーではなく継続的なレプリケーションを提供します。移行中に移行元データベースを稼働させ書き込み可能にしておく必要があるとき、または切り替えではなく継続的な同期が必要なときには、これが正しいツールです。稼働中の Kafka と Kafka Connect のクラスタが必要で、これは一回限りの移行に必要な以上のインフラです。単一の切り替えには、代わりに pg_dump を使ってください。
データ型の対応
シーケンスと AUTO_INCREMENT
PostgreSQL は自動増分の値にシーケンス (独立したオブジェクト) を使います。MySQL は列の属性としてAUTO_INCREMENT を使います。
PostgreSQL:
SELECT LAST_INSERT_ID(); を使います (PostgreSQL の currval() または RETURNING id に相当)。
SQL 方言の違い
文字列の連結:ANSI_QUOTES SQL モードが有効な場合だけです。
ILIKE (大文字小文字を区別しない LIKE):
utf8mb4_0900_ai_ci (MySQL 8.x のデフォルト) または同様の _ci 照合順序では、デフォルトで大文字小文字を区別しません。大文字小文字を区別する一致が必要な場合は、_bin 照合順序を使ってください。
RETURNING 句:
mydb.public.orders) を使います。MySQL は「スキーマ」と「データベース」を同じ意味で使い、データベース内のスキーマという概念はありません。PostgreSQL がスキーマと呼ぶものを、MySQL はデータベースと呼びます。
UNIQUE インデックスでの NULL の扱い:
PostgreSQL は一意列に複数の NULL 値を許します (NULL は互いに等しくありません)。MySQL (InnoDB) も一意インデックスに複数の NULL を許します。動作は同じです。
CTE:
MySQL は再帰 CTE を含む CTE をサポートしています。MySQL の共通テーブル式を参照してください。
ウィンドウ関数:
MySQL はウィンドウ関数をサポートしています。MySQL のウィンドウ関数を参照してください。
ストアドプロシージャと関数:
PostgreSQL は手続き型のロジックを PL/pgSQL で書き、例外処理と豊富な制御フローを備えています。MySQL は独自の手続き型 SQL 方言を使い、PL/pgSQL をサポートせず、プロシージャ本体内のセミコロンがステートメントの終わりとして解析されないように、クライアントツールで DELIMITER の変更を必要とします。
DECLARE ... HANDLER は動作が異なるため、例外処理のロジック (EXCEPTION WHEN ... ブロック) に最も多くの手直しが必要になると見込んでください。MySQL のストアドプロシージャを参照してください。
識別子の大文字小文字の区別:
PostgreSQL は引用符のない識別子を小文字に畳み込み、引用符付きの識別子 ("MyTable") を大文字小文字を区別するものとして扱います。MySQL のルールは異なります。データベース名とテーブル名が大文字小文字を区別するかどうかは、引用符ではなく、lower_case_table_names システム変数と基盤のファイルシステムに依存します。列、インデックス、ストアドルーチンの名前は、プラットフォームに関係なく、名前解決においては決して大文字小文字を区別しません。大文字小文字を区別する PostgreSQL のテーブル名に依存していたスキーマが、移行後も同じように動作すると仮定しないでください。移行先サーバーの lower_case_table_names を確認してください。
MySQL に同等物がない機能
一部の PostgreSQL の機能には直接の同等物がありません。拡張機能を持ち込む
PostgreSQL は、ネイティブに備えていない機能のためにpgcrypto、pg_trgm、uuid-ossp のような拡張機能に依存しています。素の MySQL にはこの種の機能を追加する同等の仕組みがありませんが、VillageSQL にはあります。VillageSQL Extension Framework (VEF) を使うと、INSTALL EXTENSION でカスタムの SQL 関数と型をサーバーにインストールできます。VillageSQL の拡張機能の一部は、上の PostgreSQL 拡張機能の直接の移植です。
PostgreSQL データベースがここに挙げていない拡張機能に依存している場合、まだ VillageSQL の同等物がないかもしれません。その機能が永久に失われると決めつける前に、VillageSQL ドキュメントの拡張機能カタログを確認してください。また、依存先が自分のチームが書いたカスタムの PostgreSQL 拡張機能なら、VEF はそれを移植するためのツールでもあります。
vsql-extension-builder スキルは、要件からテスト済みでドキュメント化された .veb パッケージまで、AI コーディングエージェントが VillageSQL 上でカスタム拡張機能を作り直す手順を案内します。
移行の手順
- スキーマをエクスポートします。PostgreSQL から (
pg_dump --schema-only) エクスポートし、上の型の対応を使って各テーブルを手作業で変換します。 - データをエクスポートします。PostgreSQL から CSV として (
COPY table TO '/tmp/table.csv' CSV HEADER) エクスポートします。 - データをロードします。
LOAD DATA INFILEまたはmysqlimportで MySQL にロードします。 - クエリをテストします。PostgreSQL 固有の構文をすべて見つけて書き直します。
- 件数とチェックサムを検証します。すべてのテーブルで
SELECT COUNT(*)を実行し、主要な行を抜き取り検査します。
よくある質問
MySQL は PostgreSQL の ON CONFLICT のような UPSERT をサポートしていますか?
はい、ただし構文は異なります。MySQL の UPSERT を参照してください。MySQL の INSERT ... ON DUPLICATE KEY UPDATE と REPLACE INTO は、PostgreSQL の ON CONFLICT DO UPDATE と ON CONFLICT DO NOTHING と同じユースケースをカバーします。
PostgreSQL の BOOLEAN 列は MySQL でどう扱えばよいですか?
TINYINT(1) を使ってください。true には 1、false には 0 を保存します。ほとんどの MySQL クライアントライブラリと ORM はこれを自動的に処理し、TINYINT(1) 列をブール値として提示します。BIT(1) も使えますが、TINYINT(1) のほうがツールのサポートが広いです。
トラブルシューティング
関連項目
- MySQL のスキーママイグレーション — 移行に必要な DDL の変更を実行する
- MySQL のデータ型の選択 — PostgreSQL の型を MySQL の同等物に対応付ける
- MySQL と PostgreSQL の比較: どちらを使うべきか — この移行の背景にある機能ごとの比較