メインコンテンツへスキップ

VillageSQL is a drop-in replacement for MySQL with extensions.

All examples on this page work on VillageSQL. Install Now →
PostgreSQL と MySQL は ANSI SQL の構文を共有していますが、移行にあたってデータ型、デフォルトの動作、SQL 方言の違いに注意深く向き合う必要があるほどには分岐しています。作業の大部分は、スキーマの変換と、PostgreSQL 固有の機能に依存するクエリの修正です。 このガイドは PostgreSQL から MySQL への移行を扱います。これは、VillageSQL のような MySQL 互換サーバーへ移るときの一般的な方向です。VillageSQL は、PostgreSQL スタイルの拡張機能フレームワークを上に加えた、オープンソースの MySQL のドロップイン代替です。そのフレームワークがあるからこそ、このガイドにはスキーマとデータだけでなく、PostgreSQL の拡張機能の行き先もあります。後述の拡張機能を持ち込むを参照してください。逆方向にデータを移す場合でも、以下の型の対応と方言の違いは逆向きに当てはまりますが、移行ツールのセクションは当てはまりません。

どのアプローチを使うべきか

  • 一回限りの切り替え — 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:
MySQL の同等物:
MySQL で最後に挿入された ID を取得するには、SELECT LAST_INSERT_ID(); を使います (PostgreSQL の currval() または RETURNING id に相当)。

SQL 方言の違い

文字列の連結:
文字列の引用符:
MySQL が識別子に二重引用符を受け入れるのは、ANSI_QUOTES SQL モードが有効な場合だけです。 ILIKE (大文字小文字を区別しない LIKE):
MySQL の文字列比較は、utf8mb4_0900_ai_ci (MySQL 8.x のデフォルト) または同様の _ci 照合順序では、デフォルトで大文字小文字を区別しません。大文字小文字を区別する一致が必要な場合は、_bin 照合順序を使ってください。 RETURNING 句:
LIMIT / OFFSET:
スキーマとデータベース: PostgreSQL はデータベース内のスキーマ (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 の変更を必要とします。
PL/pgSQL から MySQL の手続き型構文への自動変換はありません。各プロシージャを手作業で書き直してください。MySQL の 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 上でカスタム拡張機能を作り直す手順を案内します。

移行の手順

  1. スキーマをエクスポートします。PostgreSQL から (pg_dump --schema-only) エクスポートし、上の型の対応を使って各テーブルを手作業で変換します。
  2. データをエクスポートします。PostgreSQL から CSV として (COPY table TO '/tmp/table.csv' CSV HEADER) エクスポートします。
  3. データをロードします。LOAD DATA INFILE または mysqlimport で MySQL にロードします。
  4. クエリをテストします。PostgreSQL 固有の構文をすべて見つけて書き直します。
  5. 件数とチェックサムを検証します。すべてのテーブルで 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) のほうがツールのサポートが広いです。

トラブルシューティング

関連項目