Migrieren von Django-Apps aus anderen Datenbanken zu SQL Server

Dieser Artikel enthält Anleitungen zum Migrieren von Django-Anwendungen von PostgreSQL, MySQL oder SQLite zu SQL Server mithilfe des mssql-django Back-Ends.

Übersicht

Djangos ORM abstrahiert die meisten Unterschiede zwischen Datenbanken, aber einige Verhaltensweisen und SQL-Dialekte unterscheiden sich je nach Datenbank-Backend. In diesem Handbuch werden die wichtigsten Unterschiede behandelt, die bei der Migration zu SQL Server auftreten.

Schritt 1: Installieren von mssql-django

Installieren Sie das mssql-django Paket und die zugehörigen Abhängigkeiten:

pip install mssql-django

Stellen Sie sicher, dass der Microsoft ODBC-Treiber für SQL Server installiert ist. Siehe mssql-django installieren für plattformspezifische Anweisungen.

Schritt 2: DATABASE-Konfiguration aktualisieren

Ersetzen Sie Ihre vorhandene Datenbankkonfiguration in 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",
        },
    },
}

Schritt 3: Erstellen neuer Migrationen

Beginnen Sie mit einem sauberen Migrationsverlauf für SQL Server:

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

Important

Übertragen Sie Ihre Daten mithilfe einer Datenmigration oder eines separaten ETL-Prozesses. Versuchen Sie nicht, PostgreSQL- oder MySQL-Migrationsdateien für SQL Server auszuführen.

Wichtige Unterschiede von PostgreSQL

Merkmal PostgreSQL SQL Server (mssql-django)
Automatisch inkrementieren SERIAL / BIGSERIAL IDENTITY(1,1)
Boolescher Typ Nativ boolean bit (0 oder 1)
Textfelder text (unbegrenzt) nvarchar(max)
JSON-Unterstützung Nativ jsonb nvarchar(max) mit JSON-Funktionen (SQL Server 2016+)
Array-Felder ArrayField Wird nicht unterstützt. Verwenden Sie eine verknüpfte Tabelle oder JSON.
HStore-Felder HStoreField Wird nicht unterstützt. Verwenden Sie stattdessen JSONField.
Bereichsfelder IntegerRangeField, BigIntegerRangeField, DateRangeField, DateTimeRangeField Wird nicht unterstützt. Verwenden Sie zwei separate Felder.
Volltextsuche SearchVector, SearchRank Verwenden Sie raw SQL mit SQL Server Volltextsuche.
DISTINCT ON Unterstützt Wird nicht unterstützt. Verwenden Sie GROUP BY oder Unterabfragen.
DateTimeField mit Zeitzone timestamp with time zone datetimeoffset (when USE_TZ=True) oder datetime2

PostgreSQL-spezifische Features, die ersetzt werden sollen

Wenn Ihr Code PostgreSQL-spezifische Funktionen aus django.contrib.postgres verwendet, ersetzen Sie diese:

# 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)

Wichtige Unterschiede von MySQL

Merkmal MySQL SQL Server (mssql-django)
Automatische Inkrementierung AUTO_INCREMENT IDENTITY(1,1)
Boolescher Typ tinyint(1) bit
Textfelder longtext nvarchar(max)
JSON-Unterstützung Native JSON (5.7 und höher) nvarchar(max) mit JSON-Funktionen
Sortierung Pro Spalte konfigurierbar Instanz- oder Datenbankebene (mit der Option COLLATE überschreiben)
DateTimeField datetime(6) datetimeoffset oder datetime2

Wichtige Unterschiede von SQLite

Merkmal SQLite SQL Server (mssql-django)
Typprüfung Flexible Eingabe Strikte Typprüfung
Gleichzeitige Schreibvorgänge Eingeschränkt Vollständige Unterstützung für Parallelität
Maximale Anzahl der Verbindungen Praktisch 1 Autor Verbindungspooling mit vielen gleichzeitigen Verbindungen
DateTimeField Als Text gespeichert datetimeoffset oder datetime2

Sortierungsunterschiede

Die Sortierungseinstellung legt fest, wie SQL Server Text vergleicht und sortiert. Dies ist eine der häufigsten Quellen für unerwartetes Verhalten bei der Migration von PostgreSQL oder MySQL.

Groß- und Kleinschreibung

Die Standardsortierung von SQL Server (SQL_Latin1_General_CP1_CI_AS) berücksichtigt die Groß-/Kleinschreibung nicht. Bei PostgreSQL wird standardmäßig die Groß-/Kleinschreibung beachtet.

Dieses Verhalten bedeutet, dass Abfragen, die zuvor zwischen "Smith" und "smith" unterschieden, diese nach der Migration als gleich behandeln:

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

Wenn Ihre Anwendung auf Vergleiche angewiesen ist, bei denen die Groß-/Kleinschreibung beachtet wird, haben Sie zwei Möglichkeiten:

  • Ändern Sie die Datenbank- oder Spaltensortierung in eine Variante mit Groß-/Kleinschreibung:

    -- 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;
    
  • Verwenden Sie Djangos __exact Lookup mit einer Kollationsüberschreibung in rohem SQL für gezielte Abfragen.

Akzentsensitivität

Die Standardmäßige SQL Server Sortierung ist akzentsensitiv (AS), was dem Verhalten von PostgreSQL entspricht. Zeichen wie é und e werden als unterschiedlich behandelt. Wenn Sie akzentunabhängige Vergleiche benötigen, verwenden Sie eine Sortierfolge, die auf _AI endet.

Konfigurieren der Sortierung in mssql-django

Überschreiben Sie die Standardsortierung für Textfeld-Nachschlagevorgänge in Ihrer Datenbankkonfiguration:

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

Note

Die Option collation in mssql-django steuert die Sortierung, die in LIKE und in Vergleichsoperationen verwendet wird, die von Djangos ORM-Lookups erzeugt werden. Die Sortierung vorhandener Spalten in der Datenbank wird nicht geändert. Verwenden Sie ALTER TABLE / ALTER COLUMN Anweisungen, um die gespeicherte Spaltensortierung zu ändern. Weitere Informationen finden Sie in SQL Server Sortierungsdokumentation.

Schritt 4: Aktualisieren von benutzerdefiniertem SQL

Wenn Ihr Code rohes SQL enthält, aktualisieren Sie ihn für SQL Server Syntax:

# 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")

Allgemeine UNTERSCHIEDE bei der SQL-Syntax:

Vorgang PostgreSQL/MySQL SQL Server
Einschränken der Ergebnisse LIMIT 10 TOP 10 oder OFFSET ... FETCH NEXT ...
Zeichenfolgenverkettung \|\| (PG) / CONCAT() + oder CONCAT()
Boolesche Literale TRUE / FALSE 1 / 0
Aktueller Zeitstempel NOW() GETDATE(), SYSDATETIME()oder SYSDATETIMEOFFSET() für Zeitzonenfähige Werte
WENN NICHT VORHANDEN CREATE TABLE IF NOT EXISTS Prüfen Sie sys.objects oder verwenden Sie IF NOT EXISTS

Unterschiede bei der Transaktionsisolation

PostgreSQL verwendet MVCC (Multi-Version Concurrency Control) für seine READ COMMITTED Isolationsstufe. Leser blockieren nie Autoren und Autoren blockieren niemals Leser.

Die Standardoption von SQL Server READ COMMITTED verwendet Sperren, was bedeutet, dass Leseabfragen blockiert werden können, während sie auf den Abschluss von Schreibtransaktionen warten. Wenn in Ihrer Anwendung nach der Migration vermehrte Blockierungen auftreten, sollten Sie in Erwägung ziehen, READ COMMITTED SNAPSHOT für die Datenbank zu aktivieren:

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

Dadurch verwendet SQL Server READ COMMITTED die Zeilenversionierung (ähnlich dem MVCC von PostgreSQL) anstelle von Sperren. Leser sehen die zuletzt festgeschriebene Version einer Zeile, ohne auf aktive Schreibvorgänge zu warten.

Note

READ COMMITTED SNAPSHOT erfordert zusätzlichen tempdb Speicherplatz für Zeilenversionen. Testen Sie unter realistischer Belastung, bevor Sie die Produktion aktivieren. Weitere Informationen finden Sie unter Transaktionsverwaltung in mssql-django.

Schritt 5: Migrieren von Daten

Die Datenmigrationsstrategie hängt von der Größe des Datasets ab:

Kleine Datasets (<500 MB)

Verwenden Sie Djangos 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

Große Datasets (>500 MB)

Verwenden Sie für große Migrationen spezielle Tools, um Speicherausschöpfungs- und Timeoutprobleme zu vermeiden. Djangos ORM ist nicht das richtige Tool für Massenladungen in diesem Maßstab. Umgehen Sie es beim Datenumzug und lassen Sie Django anschließend das Schema und die Anwendungslogik verwalten.

Werkzeug Am besten geeignet für:
SQL Server-Import/Export-Assistent Lokale zu lokalen Migrationen mit einer GUI
Azure Data Factory Jede Quelle für Azure SQL, einschließlich Hybridszenarien
Azure-Datenbankmigrationsdienst Umfangreiche Migrationen mit integrierter Validierung und Rollback
mssql-python Massenkopie mit Apache Arrow Benutzerdefinierte Python-Pipelines, die einen maximalen Durchsatz zwischen SQL Server, Azure SQL-Datenbank und SQL-Datenbank in Fabric benötigen

Überprüfung nach der Migration

Überprüfen Sie nach der Migration die Konsistenz des Identitätsstartwerts für Autoinkrementspalten:

-- 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;

Wenn CurrentIdentityIdentitySeed + record_count überschreitet, neu initialisieren:

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