Django アプリを他のデータベースから SQL Server に移行する

この記事では、mssql-django バックエンドを使用して、Django アプリケーションを PostgreSQL、MySQL、または SQLite からSQL Serverに移行するためのガイダンスを提供します。

Overview

Django の ORM はほとんどのデータベースの違いを抽象化しますが、一部の動作と SQL 言語はバックエンドによって異なります。 このガイドでは、SQL Serverに移行するときに発生する主な違いについて説明します。

手順 1: mssql-django をインストールする

mssql-django パッケージとその依存関係をインストールします。

pip install mssql-django

Microsoft ODBC Driver for SQL Serverがインストールされていることを確認します。 プラットフォーム固有の手順については、 mssql-django のインストール を参照してください。

手順 2: DATABASE 構成を更新する

settings.pyで既存のデータベース構成を置き換えます。

# Example: From PostgreSQL
# DATABASES = {
#     "default": {
#         "ENGINE": "django.db.backends.postgresql",
#         "NAME": "mydb",
#     },
# }

# To SQL Server
DATABASES = {
    "default": {
        "ENGINE": "mssql",
        "NAME": "<your-database>",
        "USER": "<your-username>",
        "PASSWORD": "<your-password>",
        "HOST": "<your-server>",
        "PORT": "1433",
        "OPTIONS": {
            "driver": "ODBC Driver 18 for SQL Server",
        },
    },
}

手順 3: 新しい移行を作成する

SQL Serverのクリーンな移行履歴から始めます。

# Remove existing migration files (keep __init__.py)
# Then regenerate
python manage.py makemigrations
python manage.py migrate

Important

データ移行または個別の ETL プロセスを使用してデータを転送します。 SQL Serverに対して PostgreSQL または MySQL 移行ファイルを実行しないでください。

PostgreSQL との主な違い

特徴 PostgreSQL SQL Server (mssql-django)
自動インクリメント SERIAL / BIGSERIAL IDENTITY(1,1)
ブール型 ネイティブ boolean bit (0 または 1)
テキスト フィールド text (無制限) nvarchar(max)
JSON のサポート ネイティブ jsonb JSON 関数を使用した nvarchar(max) (SQL Server 2016 以降)
配列フィールド ArrayField サポートされていません。 関連するテーブルまたは JSON を使用します。
HStore フィールド HStoreField サポートされていません。 JSONField を代わりに使用します。
範囲フィールド IntegerRangeFieldBigIntegerRangeFieldDateRangeFieldDateTimeRangeField サポートされていません。 2 つの異なるフィールドを使用します。
フルテキスト検索 SearchVectorSearchRank SQL Serverフルテキスト検索で生 SQL を使用します。
DISTINCT ON サポートされています サポートされていません。 GROUP BYまたはサブクエリを使用します。
DateTimeField タイム ゾーン付き timestamp with time zone datetimeoffset ( USE_TZ=Trueの場合) または datetime2

置き換える PostgreSQL 固有機能

django.contrib.postgresの PostgreSQL 固有の機能をコードで使用する場合は、それらを置き換えます。

# PostgreSQL ArrayField - replace with JSONField or related table
# Before
from django.contrib.postgres.fields import ArrayField
tags = ArrayField(models.CharField(max_length=50))

# After (using JSONField)
tags = models.JSONField(default=list)

# PostgreSQL HStoreField - replace with JSONField
# Before
from django.contrib.postgres.fields import HStoreField
metadata = HStoreField()

# After
metadata = models.JSONField(default=dict)

MySQL との主な違い

特徴 MySQL SQL Server (mssql-django)
自動インクリメント AUTO_INCREMENT IDENTITY(1,1)
ブール型 tinyint(1) bit
テキスト フィールド longtext nvarchar(max)
JSON のサポート ネイティブ JSON (5.7 以降) JSON 関数を含む nvarchar(max)
Collation 列単位で構成可能 インスタンスまたはデータベース レベル ( COLLATE オプションでオーバーライド)
DateTimeField datetime(6) datetimeoffset または datetime2

SQLite との主な違い

特徴 SQLite SQL Server (mssql-django)
型の強制 柔軟な入力 厳密な型チェックの強制
同時書き込み 制限付き 並行処理の完全対応
最大接続数 実質的に 1 つのライター 多数の同時接続に対応する接続プーリング
DateTimeField テキストとして保存 datetimeoffset または datetime2

照合順序の違い

照合順序は、SQL Server でテキストを比較および並べ替えする方法を制御します。 これは、PostgreSQL または MySQL から移行するときの予期しない動作の最も一般的な原因の 1 つです。

大文字小文字の区別

SQL Serverの既定の照合順序 (SQL_Latin1_General_CP1_CI_AS) では大文字と小文字が区別されません。 PostgreSQL では、既定では大文字と小文字が区別されます。

この動作は、移行後、以前に "Smith""smith" を区別したクエリが、それらを等しいものとして扱うことを意味します。

# On PostgreSQL: returns only exact case matches
# On SQL Server (default collation): returns both "Smith" and "smith"
User.objects.filter(last_name="Smith")

アプリケーションが大文字と小文字を区別する比較に依存している場合、次の 2 つのオプションがあります。

  • データベースまたは列の照合順序を大文字と 小文字を区別するバリアントに変更します。

    -- Database-level (affects all new columns)
    ALTER DATABASE [<your-database>] COLLATE Latin1_General_CS_AS;
    
    -- Column-level (for specific columns)
    ALTER TABLE [<your-table>]
    ALTER COLUMN [<column-name>] NVARCHAR (150) COLLATE Latin1_General_CS_AS;
    
  • 対象を絞ったクエリのために、生の SQL で照合順序のオーバーライドを使用して Django の__exactルックアップを使用します

アクセント感度

既定のSQL Server照合順序は、PostgreSQL の動作と一致するアクセントセンシティブ (AS) です。 éeなどの文字は、異なるものとして扱われます。 アクセントを区別しない比較が必要な場合は、 _AIで終わる照合順序を使用します。

mssql-django で照合順序を構成する

データベース構成のテキスト フィールド参照の既定の照合順序をオーバーライドします。

DATABASES = {
    "default": {
        "ENGINE": "mssql",
        "NAME": "<your-database>",
        "OPTIONS": {
            "driver": "ODBC Driver 18 for SQL Server",
            "collation": "Latin1_General_CS_AS",  # Case-sensitive
        },
    },
}

Note

collationmssql-django オプションは、Django の ORM 参照によって生成されるLIKEおよび比較操作で使用される照合順序を制御します。 データベース内の既存の列の照合順序は変更されません。 格納されている列の照合順序を変更するには、 ALTER TABLE / ALTER COLUMN ステートメントを使用します。 詳細については、SQL Server の照合順序に関するドキュメントを参照してください。

手順 4: カスタム SQL を更新する

コードに生の SQL が含まれている場合は、SQL Server構文用に更新します。

# PostgreSQL syntax
# cursor.execute("SELECT * FROM products LIMIT 10 OFFSET 20")

# SQL Server syntax
cursor.execute("SELECT * FROM products ORDER BY id OFFSET 20 ROWS FETCH NEXT 10 ROWS ONLY")

SQL 構文の一般的な違い:

Operation PostgreSQL/MySQL SQL Server
結果を制限する LIMIT 10 TOP 10 または OFFSET ... FETCH NEXT ...
文字列連結 \|\| (PG) / CONCAT() + または CONCAT()
ブール型リテラル TRUE / FALSE 1 / 0
現在のタイムスタンプ NOW() タイムゾーン対応の値には、GETDATE()SYSDATETIME()、または SYSDATETIMEOFFSET()
存在しない場合 CREATE TABLE IF NOT EXISTS sys.objects確認または使用IF NOT EXISTS

トランザクション分離の違い

PostgreSQL では、分離レベルに MVCC (複数バージョンのコンカレンシー制御) が使用されます。 リーダーがライターをブロックしたり、ライターがリーダーをブロックしたりすることはありません。

SQL Serverの既定のREAD COMMITTEDではロックが使用されます。つまり、書き込みトランザクションの完了を待機している間に読み取りクエリがブロックされる可能性があります。 移行後にアプリケーションのブロックが増加する場合は、データベースで READ COMMITTED SNAPSHOT を有効にすることを検討してください。

ALTER DATABASE [<your-database>]
SET READ_COMMITTED_SNAPSHOT ON;

これによりSQL ServerのREAD COMMITTEDが、ロックの代わりに行のバージョン管理 (PostgreSQL の MVCC と同様) を使用するように変更されます。 閲覧者は、アクティブなライターを待たずに、行の最後にコミットされたバージョンを表示します。

Note

READ COMMITTED SNAPSHOT には、行バージョン用の追加の tempdb 領域が必要です。 運用環境で有効にする前に、現実的な負荷でテストします。 詳細については、「 mssql-django でのトランザクション管理」を参照してください。

手順 5: データを移行する

データ移行戦略は、データセットのサイズによって異なります。

小さなデータセット (<500 MB)

Django の dumpdata/loaddataを使用します。

# On the source database
python manage.py dumpdata --natural-foreign --natural-primary -o data.json

# Switch settings.py to SQL Server, then:
python manage.py migrate
python manage.py loaddata data.json

大規模なデータセット (>500 MB)

大規模な移行の場合は、特殊なツールを使用して、メモリの枯渇とタイムアウトの問題を回避します。 Django の ORM は、この規模での一括読み込みの適切なツールではありません。 データ移動のためにそれをバイパスし、後で Django がスキーマとアプリケーション ロジックを管理できるようにします。

ツール 最適な用途
SQL Server インポートおよびエクスポート ウィザード GUI を使用したオンプレミスからオンプレミスへの移行
Azureデータファクトリー ハイブリッド シナリオを含め、任意のソースから Azure SQL へ
Azure Database Migration Service 検証とロールバックが組み込まれた大規模な移行
Apache Arrow を使用した mssql-python の一括コピー FabricのSQL Server、Azure SQL Database、SQL データベース間の最大スループットを必要とするカスタム Python パイプライン

移行後の検証

移行後、自動インクリメント列の ID シード整合性を検証します。

-- Check identity seed and current value for all tables
SELECT 
    TABLE_NAME,
    IDENT_SEED(TABLE_SCHEMA + '.' + TABLE_NAME) AS IdentitySeed,
    IDENT_INCR(TABLE_SCHEMA + '.' + TABLE_NAME) AS IdentityIncrement,
    IDENT_CURRENT(TABLE_SCHEMA + '.' + TABLE_NAME) AS CurrentIdentity
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_TYPE = 'BASE TABLE'
    AND OBJECTPROPERTY(OBJECT_ID(TABLE_SCHEMA + '.' + TABLE_NAME), 'TableHasIdentity') = 1
ORDER BY TABLE_NAME;

CurrentIdentityIdentitySeed + record_count を超えた場合: 再シードしてください。

DBCC CHECKIDENT ('your_table', RESEED, new_seed);