Nota:
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
SQLite es la base de datos predeterminada para muchos proyectos de Python, FastAPI y las aplicaciones Flask. Te permite construir tu aplicación rápidamente retrasando la decisión de la plataforma de datos hasta más adelante. Cuando llegue el momento de llevar tu aplicación a producción, necesitas soporte para usuarios concurrentes, seguridad basada en roles, alta disponibilidad, recuperación ante desastres y otras funciones empresariales. Necesitas migrar a Microsoft SQL usando el controlador mssql-python.
Diferencias entre dialectos de SQL
Cuando migras de SQLite a Microsoft SQL, necesitas abordar dos cosas: reescribir tus sentencias SQL para Transact-SQL (T-SQL) y migrar tus datos.
La siguiente tabla mapea patrones comunes de SQLite a sus equivalentes en Microsoft SQL:
| SQLite | SQL Server (T-SQL) | Notas |
|---|---|---|
INTEGER PRIMARY KEY AUTOINCREMENT |
int IDENTITY(1,1) PRIMARY KEY |
Microsoft SQL usa IDENTITY para autoincremento. |
TEXT |
nvarchar(255) o nvarchar(max) |
Indica siempre una longitud. Usa nvarchar para Unicode. |
REAL |
float o decimal(18,2) |
Úsalo decimal para valores exactos como el dinero. |
BLOB |
varbinary(max) |
Mismo comportamiento, nombre diferente. |
BOOLEAN (almacenado como ENTERO) |
bit |
Ninguna de las dos bases de datos tiene un booleano nativo. Ambos almacenan 0/1. |
DATETIME('now') |
GETDATE() o SYSDATETIME() |
SYSDATETIME() Proporciona mayor precisión. |
LIMIT 10 OFFSET 20 |
OFFSET 20 ROWS FETCH NEXT 10 ROWS ONLY |
Requiere una ORDER BY cláusula. |
\|\| (concat de cadena) |
+ o CONCAT() |
CONCAT() gestiona NULL valores. |
IFNULL(a, b) |
ISNULL(a, b) o COALESCE(a, b) |
COALESCE es estándar ANSI. |
GROUP_CONCAT(col) |
STRING_AGG(col, ',') |
Disponible en SQL Server 2017+. |
INSERT OR REPLACE INTO |
Instrucción MERGE |
SQLite elimina y vuelve a insertar; MERGE actualiza en el mismo lugar. Véase el ejemplo que sigue. |
last_insert_rowid() |
OUTPUT INSERTED.id |
Usa OUTPUT en la sentencia INSERT.
SCOPE_IDENTITY() también funciona, pero requiere un archivo separado SELECT. |
typeof(x) |
SQL_VARIANT_PROPERTY(x, 'BaseType') |
Rara vez se necesita con el tipado estricto. |
CREATE TABLE ejemplo
-- SQLite
CREATE TABLE IF NOT EXISTS products (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
price REAL DEFAULT 0.0,
created_at TEXT DEFAULT (datetime('now')),
is_active BOOLEAN DEFAULT 1
);
-- SQL Server
IF NOT EXISTS (SELECT * FROM sys.tables WHERE name = 'products')
CREATE TABLE products (
id int IDENTITY(1,1) PRIMARY KEY,
name nvarchar(100) NOT NULL,
price decimal(10,2) DEFAULT 0.0,
created_at datetime2 DEFAULT SYSDATETIME(),
is_active bit DEFAULT 1
);
Ejemplos de consultas
Paginación:
SQLite:
cursor.execute("SELECT * FROM products ORDER BY name LIMIT ? OFFSET ?", (10, 20))
MSSQL-Python:
cursor.execute(
"SELECT * FROM Production.Product ORDER BY Name OFFSET ? ROWS FETCH NEXT ? ROWS ONLY",
(20, 10)
)
El orden de los parámetros se invierte. Transact-SQL pone OFFSET antes FETCH NEXTde .
Upsert (insertar o actualizar):
SQLite:
cursor.execute("""
INSERT OR REPLACE INTO settings (key, value)
VALUES (?, ?)
""", (key, value))
MSSQL-Python:
cursor.execute("""
MERGE #Settings AS target
USING (SELECT ? AS [key], ? AS value) AS source
ON target.[key] = source.[key]
WHEN MATCHED THEN UPDATE SET value = source.value
WHEN NOT MATCHED THEN INSERT ([key], value) VALUES (source.[key], source.value);
""", (key, value))
La USING cláusula define source.[key] y source.value como alias de columna. Las WHEN cláusulas hacen referencia a esos alias. Solo se necesitan dos ? marcadores.
Último ID insertado:
SQLite:
cursor.execute("INSERT INTO products (name) VALUES (?)", ("Widget",))
product_id = cursor.lastrowid
MSSQL-Python:
cursor.execute(
"INSERT INTO #Products (Name) OUTPUT INSERTED.ProductID VALUES (?)",
("Widget",)
)
product_id = cursor.fetchval()
Actualizar código de conexión
Reemplace sqlite3.connect() con mssql_python.connect().
SQLite:
import sqlite3
def get_connection():
conn = sqlite3.connect("myapp.db")
conn.row_factory = sqlite3.Row
return conn
MSSQL-Python:
import mssql_python
def get_connection():
return mssql_python.connect(
"Server=<server>.database.windows.net;"
"Database=<database>;"
"Authentication=ActiveDirectoryDefault;"
"Encrypt=yes;"
)
El acceso a filas funciona de forma similar. La factoría Row de SQLite devuelve filas similares a diccionarios con la sintaxis row["column"]. El controlador mssql-python devuelve objetos Row que admiten el mismo acceso mediante claves de cadena, además de acceso por atributo y por índice:
SQLite (con row_factory):
row["name"]
MSSQL-Python:
row["name"] # String-key access, like SQLite
row.name # Attribute access
row[0] # Index access
Estilo del parámetro de actualización
Tanto SQLite como mssql-python usan ? como marcador de parámetro, por lo que la mayoría de las consultas funcionan sin cambios.
# Works with both sqlite3 and mssql-python
cursor.execute("SELECT * FROM Production.Product WHERE Name = ?", ("Adjustable Race",))
La única diferencia: SQLite permite parámetros con nombre con :name sintaxis. El controlador mssql-python utiliza %(name)s en su lugar.
SQLite:
cursor.execute("SELECT * FROM products WHERE id = :id", {"id": 42})
MSSQL-Python:
cursor.execute("SELECT * FROM Production.Product WHERE ProductID = %(id)s", {"id": 42})
Migrar datos existentes
Consideraciones importantes al migrar datos de SQLite a Microsoft SQL:
- Microsoft SQL almacena los valores nvarchar(max) y varbinary(max) como objetos grandes (LOBs), que son más lentos de leer y escribir que los datos en línea. Mantén las columnas de cadena en nvarchar(4000) o menos siempre que tus datos lo permitan. SQL Server almacena estos valores directamente en la fila de datos, evitando la sobrecarga de LOB.
- El mapeo de tipos sigue las reglas de afinidad de tipos de SQLite. Revisa las tablas generadas tras la migración para ajustar el tamaño de las columnas (por ejemplo, nvarchar(100) en lugar de nvarchar(4000)) o añadir restricciones que SQLite no aplicaba.
- Para las tablas SQLite que usan TEXT para almacenar fechas, puede que necesites analizar los valores en objetos Python
datetimeantes de insertarlos. Microsoft SQL espera valores de fecha y hora adecuados, no cadenas de texto.
Utiliza este script para leer el esquema y los datos de SQLite y crear tablas coincidentes en Microsoft SQL:
import sqlite3
import mssql_python
# SQLite type affinity -> Microsoft SQL type
# Keywords follow SQLite's type affinity rules
TYPE_MAP = {
"INT": "bigint",
"CHAR": "nvarchar(4000)",
"CLOB": "nvarchar(max)",
"TEXT": "nvarchar(4000)",
"BLOB": "varbinary(max)",
"REAL": "float",
"FLOA": "float",
"DOUB": "float",
}
def map_type(sqlite_type: str) -> str:
"""Map a SQLite column type to a Microsoft SQL type."""
upper = (sqlite_type or "TEXT").upper()
for prefix, sql_type in TYPE_MAP.items():
if prefix in upper:
return sql_type
return "decimal(18,6)" # NUMERIC affinity (default)
# Connect to both databases
sqlite_conn = sqlite3.connect("myapp.db")
sql_conn = mssql_python.connect(
"Server=<server>.database.windows.net;"
"Database=<database>;"
"Authentication=ActiveDirectoryDefault;"
"Encrypt=yes;"
)
# Get list of tables from SQLite
sqlite_cur = sqlite_conn.cursor()
sqlite_cur.execute(
"SELECT name FROM sqlite_master "
"WHERE type='table' AND name NOT LIKE 'sqlite_%'"
)
tables = [row[0] for row in sqlite_cur.fetchall()]
sql_cursor = sql_conn.cursor()
for table in tables:
# Read column info from SQLite
sqlite_cur.execute(f"PRAGMA table_info([{table}])")
columns = sqlite_cur.fetchall()
# columns: (cid, name, type, notnull, default_value, pk)
# Build CREATE TABLE statement
col_defs = []
for col in columns:
name, col_type, notnull, pk = col[1], col[2], col[3], col[5]
sql_type = map_type(col_type)
parts = [f"[{name}] {sql_type}"]
if notnull:
parts.append("NOT NULL")
if pk:
parts.append("PRIMARY KEY")
col_defs.append(" ".join(parts))
create_sql = f"CREATE TABLE [{table}] ({', '.join(col_defs)})"
sql_cursor.execute(
f"IF OBJECT_ID('{table}','U') IS NULL " + create_sql
)
sql_conn.commit()
# Read all rows from SQLite
sqlite_cur.execute(f"SELECT * FROM [{table}]")
rows = sqlite_cur.fetchall()
if not rows:
print(f" {table}: created (empty)")
continue
# Use bulkcopy for fast insert
result = sql_cursor.bulkcopy(table, rows)
print(f" {table}: {result['rows_copied']} rows copied")
sql_conn.commit()
sqlite_conn.close()
sql_conn.close()
Diferencias de características
Tras la migración, tu aplicación obtiene acceso a funciones de Microsoft SQL que SQLite no soporta:
| Feature | SQLite | SQL Server |
|---|---|---|
| Escrituras simultáneas | Escritor solitario a la vez | Concurrencia total con bloqueo a nivel de fila |
| Autenticación | Solo permisos de archivo | autenticación SQL, autenticación de Windows, Microsoft Entra ID |
| Procedimientos almacenados | No soportado | Programación completa en T-SQL |
| Encryption | No integrado | TLS en tránsito, TDE en reposo |
| Transactions | Puntos de salvaguarda, niveles básicos de aislamiento | Niveles de aislamiento completo, transacciones distribuidas |
| Compatibilidad con JSON | json_extract() |
OPENJSON(), JSON_VALUE(), FOR JSON |
| Búsqueda de texto completo | Extensión FTS5 | Indexación de texto completo incorporada |
| Tamaño máximo de base de datos | ~281 TB (límite práctico inferior) | 524 PB |
| Agrupación de conexiones | N/A (en proceso) | Integrado con mssql-python |