SQLite是许多Python项目、FastAPI和Flask应用的默认数据库。 它让你可以通过延迟数据平台的决策来快速构建应用。 当你的应用需要上线时,你需要支持并发用户、基于角色的安全性、高可用性、灾难恢复以及其他企业特性。 你需要用 mssql-python 驱动迁移到 Microsoft SQL。
SQL 方言差异
当你从 SQLite 迁移到 Microsoft SQL 时,你需要解决两件事:重写 Transact-SQL 的 SQL 语句(T-SQL)和迁移数据。
下表将常见的SQLite模式映射到Microsoft SQL的对应格式:
| SQLite | SQL Server (T-SQL) | 注释 |
|---|---|---|
INTEGER PRIMARY KEY AUTOINCREMENT |
int IDENTITY(1,1) PRIMARY KEY |
Microsoft SQL 使用 IDENTITY 实现自动递增。 |
TEXT |
nvarchar(255) 或 nvarchar(max) |
一定要指定长度。 对 Unicode 使用 nvarchar。 |
REAL |
float 或 decimal(18,2) |
用 decimal 来指准确的数值,比如钱。 |
BLOB |
varbinary(max) |
行为一样,名字不同。 |
BOOLEAN (以整数形式存储) |
bit |
这两个数据库都没有原生布尔值。 两者都存储 0/1。 |
DATETIME('now') |
GETDATE() 或 SYSDATETIME() |
SYSDATETIME() 提供更高的精度。 |
LIMIT 10 OFFSET 20 |
OFFSET 20 ROWS FETCH NEXT 10 ROWS ONLY |
需要一个 ORDER BY 条款。 |
\|\| (字符串拼接) |
+ 或 CONCAT() |
CONCAT() 处理 NULL 数值。 |
IFNULL(a, b) |
ISNULL(a, b) 或 COALESCE(a, b) |
COALESCE 是ANSI标准。 |
GROUP_CONCAT(col) |
STRING_AGG(col, ',') |
可在 SQL Server 2017+ 中获得。 |
INSERT OR REPLACE INTO |
MERGE 语句 |
SQLite 删除并重新插入; MERGE 更新正在进行中。 请参见以下示例。 |
last_insert_rowid() |
OUTPUT INSERTED.id |
在INSERT语句中使用OUTPUT。
SCOPE_IDENTITY() 也可以,但需要单独的 SELECT。 |
typeof(x) |
SQL_VARIANT_PROPERTY(x, 'BaseType') |
在严格类型检查下,很少需要这样做。 |
CREATE TABLE 示例
-- 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
);
查询示例
页码:
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)
)
参数顺序相反。 Transact-SQL 置 OFFSET 于 FETCH NEXT。
Upsert(插入或更新):
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))
USING 子句将 source.[key] 和 source.value 定义为列别名。 这些 WHEN 条款指的是这些别名。 只需要两个 ? 标记。
最后输入的身份证:
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()
更新连接代码
将sqlite3.connect()替换为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;"
)
行访问的工作方式类似。 SQLite 的 Row 工厂函数返回类似字典的行,并可使用 row["column"] 语法访问。 mssql-python 驱动返回 Row 对象,这些对象支持相同的字符串键访问方式,以及属性访问和索引访问:
SQLite(含 row_factory):
row["name"]
MSSQL-python:
row["name"] # String-key access, like SQLite
row.name # Attribute access
row[0] # Index access
更新参数样式
SQLite 和 mssql-python 都用作 ? 参数标记,所以大多数查询无需更改即可完成。
# Works with both sqlite3 and mssql-python
cursor.execute("SELECT * FROM Production.Product WHERE Name = ?", ("Adjustable Race",))
唯一的区别是:SQLite 允许带 :name 语法的命名参数。 mssql-python 驱动程序则使用 %(name)s 代替。
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})
迁移现有数据
从SQLite迁移到Microsoft SQL时的重要考虑因素:
- Microsoft SQL 将 nvarchar(max)和 varbinary(max)值存储为大型对象(LOB),这些值的读写速度比行内数据慢。 只要数据允许,保持字符串列在 nvarchar(4000) 或更低。 SQL Server 将这些值直接存储在数据行中,避免了 LOB 开销。
- 类型映射遵循SQLite的 类型亲和规则。 迁移后检查生成的表,缩小列大小(例如将 nvarchar(100) 取代 nvarchar(4000))或添加SQLite未强制执行的约束。
- 对于用TEXT存储日期的SQLite表,你可能需要在插入前把数值解析成Python
datetime对象。 Microsoft SQL 要求的是正确的日期时间值,而不是文本字符串。
使用该脚本读取 SQLite 中的模式和数据,并在 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()
功能差异
迁移后,您的应用程序将获得 SQLite 不支持的 Microsoft SQL 功能访问权限:
| Feature | SQLite | SQL Server |
|---|---|---|
| 并发写入 | 一次只写一个作家 | 采用行级锁定实现完全并发 |
| Authentication | 仅限文件权限 | SQL 身份验证,Windows 身份验证,Microsoft Entra ID |
| 存储过程 | 不支持 | 完整的 T-SQL 可编程性 |
| Encryption | 不是内置的 | TLS运输中,TDE静止状态 |
| Transactions | 保存点,基本隔离级别 | 完全隔离级别,分布式事务 |
| JSON 支持 | json_extract() |
OPENJSON()、JSON_VALUE()、FOR JSON |
| 全文搜索 | FTS5扩展 | 内置全文索引 |
| 最大数据库大小 | ~281 TB(实际可用上限更低) | 524PB |
| 连接池 | 不适用(处理中) | mssql-python 内置 |