SQLite和SQL Server虽然都支持SQL,但前者是嵌入式轻量数据库,后者是面向企业级应用的关系型数据库,二者在T-SQL方言、数据类型处理、分页查询和系统函数等方面存在明显差异。从SQL Server迁移到SQLite的项目,如果没有做好语法兼容,运行时很容易出现函数不存在、类型不匹配或分页语句失效等问题。本文将围绕这些关键差异展开,并给出可实际运行的对比示例。

需要先明确一个概念:SQLite本身并不支持完整的T-SQL扩展。SQL Server中的T-SQL是一套包含流程控制、变量、存储过程、错误处理和大量内置函数的方言,而SQLite的核心SQL更偏向通用SQL标准,并且通过C接口提供少量标量函数和聚合函数。因此,讨论两者差异时,既要关注常用查询语法,也要考虑数据库架构和部署形态带来的限制。
一、数据类型与存储模型差异
SQLite采用动态类型系统,也被称为类型亲和性。建表时列声明为TEXT,实际仍然可以存储整数或浮点数,SQLite会根据值的格式进行存储,但会记录类型亲和性作为参考。SQL Server则采用强类型约束,列声明为NVARCHAR后,写入整数会报错或发生隐式转换。这个差异在迁移时需要特别留意,尤其是那些依赖非法类型写入的旧系统,可能在SQLite中能运行,迁移到SQL Server后会直接失败。
在自增整数主键上,两者的写法差别很大。SQLite使用INTEGER PRIMARY KEY即可实现自增,其背后实际是rowid的别名;SQL Server则需要使用IDENTITY(1,1)来声明自增列。下面分别给出建表示例。
-- SQLite 建表
CREATE TABLE employee (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
salary REAL,
is_active INTEGER DEFAULT 1
);
-- SQL Server 建表
CREATE TABLE employee (
id INT IDENTITY(1,1) PRIMARY KEY,
name NVARCHAR(50) NOT NULL,
salary FLOAT,
is_active BIT DEFAULT 1
);
布尔类型也存在差异。SQLite没有独立的布尔类型,通常用INTEGER存储0和1;SQL Server提供BIT类型,取值范围为0、1或NULL。日期类型方面,SQLite可以把日期保存为TEXT、REAL或INTEGER,没有专门的日期时间类型;SQL Server则有DATE、DATETIME、DATETIME2等类型。了解这些类型差异,能避免迁移后出现精度丢失或格式错乱。
| 数据类型或场景 | SQLite | SQL Server |
|---|---|---|
| 整数自增主键 | INTEGER PRIMARY KEY | INT IDENTITY(1,1) PRIMARY KEY |
| 布尔值 | INTEGER 0/1 | BIT |
| 日期时间 | TEXT/REAL/INTEGER | DATE/DATETIME/DATETIME2 |
| 浮点数 | REAL | FLOAT/REAL |
| 大文本 | TEXT | NVARCHAR(MAX)/VARCHAR(MAX) |
从表格可以看出,SQLite更倾向于简化类型体系,适合资源受限的客户端或移动端;SQL Server则通过丰富的类型系统满足企业级数据建模需求。开发者在设计兼容层时,可以先建立类型映射表,再根据语义选择最合适的替代类型。
二、字符串、日期与空值处理差异
字符串拼接是日常查询中的高频操作。SQLite使用双竖线||作为拼接操作符,SQL Server则使用加号+进行字符串拼接。如果SQL Server脚本中混用数字和字符串,加号可能触发数值加法,导致结果不符合预期,而SQLite的||始终把操作数按字符串处理。SQL Server还提供了CONCAT函数,它在遇到NULL时会自动忽略,而SQLite没有内置的CONCAT函数,需要借助||或printf实现。
-- SQLite 字符串处理
SELECT 'Hello ' || 'World' AS greeting;
SELECT substr(name, 1, 3), length(name), upper(name)
FROM employee;
SELECT date('now'), datetime('now', '+1 day');
-- SQL Server 字符串处理 SELECT 'Hello ' + 'World' AS greeting; SELECT SUBSTRING(name, 1, 3), LEN(name), UPPER(name) FROM employee; SELECT GETDATE(), DATEADD(DAY, 1, GETDATE());
函数名称上也有不少差异。SQLite用substr()截取子串,SQL Server用SUBSTRING();SQLite用length()计算字符串长度,SQL Server用LEN()。日期函数差异更大,SQLite通过date()、datetime()、strftime()处理时间,SQL Server则依赖GETDATE()、DATEADD()、DATEDIFF()和CONVERT()。迁移日期查询时,通常需要重写表达式,不能只做简单替换。
处理空值时,SQLite提供IFNULL(expr, replacement)和COALESCE(),SQL Server提供ISNULL(expr, replacement)和COALESCE()。COALESCE两者都支持,是更通用的选择;但IFNULL和ISNULL互为方言函数,不能跨库使用。例如把经理编号为空的值转换为0,两边写法如下。
-- SQLite 空值处理 SELECT IFNULL(manager_id, 0) FROM employee;
-- SQL Server 空值处理 SELECT ISNULL(manager_id, 0) FROM employee;
需要特别注意的是,ISNULL在SQL Server中还可能出现在布尔判断中,而SQLite的IFNULL只能用于表达式替换。遇到复杂逻辑时,建议统一使用COALESCE,这样可以减少方言耦合,提升SQL的可移植性。
三、分页与自增主键实现差异
分页查询是迁移过程中最容易出错的部分。SQLite从很早开始就支持LIMIT和OFFSET,写起来非常简洁。SQL Server则在较早版本中使用TOP配合子查询或ROW_NUMBER()实现分页,新版本才引入OFFSET ... FETCH语法。如果直接把SQLite的分页语句放到旧版SQL Server上运行,会直接报语法错误。
-- SQLite 查询第3页,每页10条 SELECT id, name FROM employee ORDER BY id LIMIT 10 OFFSET 20;
-- SQL Server 查询第3页,每页10条 SELECT id, name FROM employee ORDER BY id OFFSET 20 ROWS FETCH NEXT 10 ROWS ONLY;
SQL Server使用OFFSET ... FETCH时必须包含ORDER BY,否则会报错。SQLite则没有这个强制要求,但缺少ORDER BY时返回顺序不稳定。因此,在迁移分页SQL时,最好显式加上排序字段,既保证结果可预期,也符合SQL Server的语法要求。如果还要兼容旧版SQL Server,可以继续保留ROW_NUMBER()方案。
自增主键的插入后取值也有不同写法。SQLite在插入完成后可以调用last_insert_rowid()获取最后插入行的rowid;SQL Server可以使用SCOPE_IDENTITY()或OUTPUT INSERTED.id。下面的示例展示了两种获取新主键的方式。
-- SQLite 插入并获取自增ID
INSERT INTO employee(name, salary) VALUES('Tom', 8000);
SELECT last_insert_rowid();
-- SQL Server 插入并获取自增ID
INSERT INTO employee(name, salary) OUTPUT INSERTED.id VALUES('Tom', 8000);
-- 或者使用 SCOPE_IDENTITY()
INSERT INTO employee(name, salary) VALUES('Tom', 8000);
SELECT SCOPE_IDENTITY();
在批量插入或触发器场景下,SCOPE_IDENTITY()返回当前作用域内的最后一个标识值,@@IDENTITY则不受作用域限制,可能返回触发器产生的主键。SQLite的last_insert_rowid()是连接级别的,每个连接独立。迁移时如果涉及触发器插入,必须理清主键来源,否则取到错误ID会引发数据关联问题。
四、约束、事务与架构能力差异
外键约束在SQLite中默认不启用。即使建表时声明了FOREIGN KEY,如果不执行PRAGMA foreign_keys = ON;,插入违反外键的数据也不会被拒绝。SQL Server则默认强制启用外键约束,任何违反约束的操作都会直接报错。因此,把SQL Server数据库迁移到SQLite后,如果忘记开启外键检查,可能埋下数据完整性隐患。
-- SQLite 开启外键约束 PRAGMA foreign_keys = ON; -- 查询当前外键状态,返回1表示开启 PRAGMA foreign_keys;
事务与并发控制方面,SQLite使用数据库级写锁,同一时间只允许一个写事务,读操作可以在不加锁的情况下进行,但写入竞争较大时性能会明显下降。SQL Server采用行级锁、页锁和表锁相结合的机制,支持高并发读写场景。对于多用户同时写入的系统,SQLite并不适合承担核心数据库角色;但在单机应用、移动端缓存或测试环境中,SQLite的小体积和零部署优势非常突出。
SQLite的ALTER TABLE能力也相对有限。添加列通常支持,但修改列类型或删除列在旧版本中比较困难,往往需要建新表并复制数据。SQL Server则可以通过ALTER TABLE ALTER COLUMN、DROP COLUMN等命令灵活调整结构。此外,SQLite不支持T-SQL存储过程、用户定义类型和完整的游标语法,其触发器支持也比较基础。迁移架构时,需要把存储过程逻辑提到应用层,或用视图、触发器配合实现一部分数据库端逻辑。
总体而言,SQLite与SQL Server的T-SQL差异主要集中在类型系统、内置函数、分页语法、自增取值和约束机制五个方面。用SQLite做开发和测试可以降低环境成本,但正式发布到SQL Server时,必须逐条审查SQL方言兼容性;反向迁移时则要简化存储过程和高级类型,并把外键开关纳入连接初始化流程。理解这些差异,才能在两个数据库之间高效迁移并保持业务逻辑稳定。
SQLiteSQL ServerT-SQL差异修改时间:2026-08-19 15:25:55