Nota
O acesso a esta página requer autorização. Pode tentar iniciar sessão ou alterar os diretórios.
O acesso a esta página requer autorização. Pode tentar alterar os diretórios.
O SQLite é a base de dados padrão para muitos projetos Python, FastAPI e aplicações Flask. Permite-lhe construir a sua aplicação rapidamente, adiando a decisão da plataforma de dados para mais tarde. Quando chegar a altura de levar a sua aplicação para produção, precisa de suporte para utilizadores concorrentes, segurança baseada em funções, alta disponibilidade, recuperação de desastres e outras funcionalidades empresariais. Precisas de migrar para Microsoft SQL usando o driver mssql-python.
Diferenças nos dialetos SQL
Quando migra de SQLite para Microsoft SQL, precisa de abordar duas coisas: reescrever as suas instruções SQL para Transact-SQL (T-SQL) e migrar os seus dados.
A tabela seguinte mapeia padrões SQLite comuns para os seus equivalentes Microsoft SQL:
| SQLite | SQL Server (T-SQL) | Notes |
|---|---|---|
INTEGER PRIMARY KEY AUTOINCREMENT |
int IDENTITY(1,1) PRIMARY KEY |
O Microsoft SQL utiliza IDENTITY para incremento automático. |
TEXT |
nvarchar(255) ou nvarchar(max) |
Especifique sempre um comprimento. Utilize nvarchar para Unicode. |
REAL |
float ou decimal(18,2) |
Use decimal para valores exatos como dinheiro. |
BLOB |
varbinary(max) |
Mesmo comportamento, nome diferente. |
BOOLEAN (armazenado como INTEGER) |
bit |
Nenhuma das bases de dados tem um booleano nativo. Ambos armazenam 0/1. |
DATETIME('now') |
GETDATE() ou SYSDATETIME() |
SYSDATETIME() Dá maior precisão. |
LIMIT 10 OFFSET 20 |
OFFSET 20 ROWS FETCH NEXT 10 ROWS ONLY |
Requer uma cláusula ORDER BY. |
\|\| (concatenação de cadeias de caracteres) |
+ ou CONCAT() |
CONCAT() processa NULL valores. |
IFNULL(a, b) |
ISNULL(a, b) ou COALESCE(a, b) |
COALESCE é padrão ANSI. |
GROUP_CONCAT(col) |
STRING_AGG(col, ',') |
Disponível no SQL Server 2017+. |
INSERT OR REPLACE INTO |
Declaração MERGE |
SQLite apaga e reinsere; MERGE atualiza no local. Veja o exemplo que se segue. |
last_insert_rowid() |
OUTPUT INSERTED.id |
Use OUTPUT na INSERT declaração.
SCOPE_IDENTITY() também funciona, mas requer um SELECT separado. |
typeof(x) |
SQL_VARIANT_PROPERTY(x, 'BaseType') |
Raramente necessário com uma digitação rigorosa. |
CREATE TABLE exemplo
-- 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
);
Exemplos de consultas
Paginação:
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)
)
A ordem dos parâmetros é invertida. Transact-SQL põe OFFSET antes FETCH NEXTde .
Upsert (inserir ou atualizar):
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))
A USING cláusula define source.[key] e source.value como alias de coluna. As WHEN cláusulas referem-se a esses pseudónimos. São necessários apenas dois ? marcadores.
Último ID inserido:
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()
Atualizar código de ligação
Substituir sqlite3.connect() por 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;"
)
O acesso por linha funciona de modo semelhante. A função de criação do SQLite Row retorna linhas semelhantes a dicionários com a sintaxe row["column"]. O driver mssql-python retorna objetos Row que suportam o mesmo acesso por chave de string, bem como acesso por atributo e por índice:
SQLite (com row_factory):
row["name"]
mssql-python:
row["name"] # String-key access, like SQLite
row.name # Attribute access
row[0] # Index access
Estilo de atualização do parâmetro
Tanto o SQLite como o mssql-python usam ? como marcador de parâmetro, pelo que a maioria das consultas funciona sem alterações.
# Works with both sqlite3 and mssql-python
cursor.execute("SELECT * FROM Production.Product WHERE Name = ?", ("Adjustable Race",))
A única diferença: o SQLite permite parâmetros nomeados com :name sintaxe. O driver mssql-python usa %(name)s em vez disso.
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 dados existentes
Considerações importantes ao migrar dados do SQLite para o Microsoft SQL:
- O Microsoft SQL armazena os valores nvarchar(max) e varbinary(max) como objetos grandes (LOBs), que são mais lentos a ler e escrever do que os dados em linha. Mantenha as colunas de cadeias de caracteres com nvarchar(4000) ou menos sempre que os seus dados o permitam. O SQL Server armazena estes valores diretamente na linha de dados, evitando a sobrecarga do LOB.
- O mapeamento de tipos segue as regras de afinidade de tipos do SQLite. Revê as tabelas geradas após a migração para restringir o tamanho das colunas (por exemplo, nvarchar(100) em vez de nvarchar(4000)) ou adicionar restrições que o SQLite não aplicou.
- Para tabelas SQLite que usam TEXT para armazenar datas, pode ser necessário analisar os valores em objetos Python
datetimeantes de inserir. O Microsoft SQL espera valores de data-hora corretos, não cadeias de texto.
Use este script para ler o esquema e os dados do SQLite e criar tabelas correspondentes no 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()
Diferenças entre caraterísticas
Após a migração, a sua aplicação ganha acesso a funcionalidades do Microsoft SQL que o SQLite não suporta:
| Feature | SQLite | SQL Server |
|---|---|---|
| Gravações simultâneas | Apenas um escritor de cada vez | Concorrência total com bloqueio ao nível das filas |
| Authentication | Apenas permissões de ficheiro | autenticação SQL, autenticação do Windows, Microsoft Entra ID |
| Procedimentos armazenados | Não suportado | Programabilidade total em T-SQL |
| Encryption | Não incorporado | TLS em trânsito, TDE em repouso |
| Transações | Pontos de gravação, níveis básicos de isolamento | Níveis completos de isolamento, transações distribuídas |
| Suporte a JSON | json_extract() |
OPENJSON(), JSON_VALUE(), FOR JSON |
| Pesquisa em texto completo | Extensão FTS5 | Indexação de texto integral incorporada |
| Tamanho máximo do banco de dados | ~281 TB (limite prático inferior) | 524 Petabytes |
| Agrupamento de conexões | N/A (em processo) | Incorporado com mssql-python |