PostgreSQL vs SQL Server:语法差异与迁移避坑指南

为什么又双叒要迁移数据库

很多团队从 SQL Server 迁到 PostgreSQL,原因无外乎:授权成本、跨平台(Linux 原生友好)、开源生态、云厂商支持。但迁移最难的不是「把数据搬过去」,而是两套方言的语法差异——一个写惯了 TOPGETDATE() 的人,第一次写 PG 会被 LIMITNOW() 之外的一堆细节绊倒。

本文按「高频差异 → 迁移踩坑」梳理,帮你少踩坑。

一、自增主键:IDENTITY vs SERIAL / GENERATED

-- SQL Server
CREATE TABLE users (
    id INT IDENTITY(1,1) PRIMARY KEY,
    name NVARCHAR(100)
);
INSERT INTO users(name) VALUES('张三');  -- 不用管 id

-- PostgreSQL(推荐 GENERATED AS IDENTITY,语义最贴近 IDENTITY)
CREATE TABLE users (
    id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
    name VARCHAR(100)
);
-- 老写法 SERIAL 也可用,但 GENERATED 更符合标准、可控更强

二、分页:TOP vs LIMIT/OFFSET

-- SQL Server
SELECT TOP 10 * FROM orders ORDER BY id DESC;

-- PostgreSQL
SELECT * FROM orders ORDER BY id DESC LIMIT 10 OFFSET 20;

深分页性能上两者都要注意,大数据量建议用「游标分页」(基于上一页最后 id)。

三、字符串与日期函数对照

场景 SQL Server PostgreSQL
当前时间 GETDATE() / SYSDATETIME() NOW() / CURRENT_TIMESTAMP
字符串拼接 'a' + 'b'(注意 NULL 变 NULL) 'a' || 'b'
判空合并 ISNULL(a,b) COALESCE(a,b)
substring SUBSTRING(s,1,3) SUBSTRING(s,1,3)(同)
类型转换 CONVERT(VARCHAR, x) x::VARCHARCAST(x AS VARCHAR)
获取日期部分 DATEPART(yy, d) EXTRACT(YEAR FROM d)

四、存储过程与匿名块

SQL Server 的 CREATE PROCEDURE 与 PG 的 CREATE FUNCTION/PROCEDURE 语义相近,但语法细节差异大。PG 更强调函数返回类型(RETURNS TABLE 等)。大量依赖存储过程的老系统,这块是迁移工作量大头。

-- PostgreSQL 函数示例
CREATE FUNCTION get_active_users() RETURNS TABLE(id INT, name VARCHAR) AS $$
BEGIN
    RETURN QUERY SELECT u.id, u.name FROM users u WHERE u.active = true;
END;
$$ LANGUAGE plpgsql;

五、其他高频坑

  • 标识符大小写:PG 默认把未加引号的标识符转小写,SQL Server 不敏感。迁移后注意表名/列名大小写。
  • NULL 与空字符串:PG 区分 ''NULL,SQL Server 的 '' 在某些比较里接近 NULL,逻辑要重写。
  • 临时表:SQL Server #temp 与会话绑定;PG 用 CREATE TEMPORARY TABLE,注意生命周期。
  • 锁与隔离级别:两者默认隔离级别不同(SQL Server 读已提交快照 vs PG 读已提交),高并发逻辑需复核。

六、迁移工具与流程建议

  1. 结构迁移:用 pgloaderSQL Server Migration Assistant (SSMA)ora2pg 思路的工具做 schema 转换,但务必人工校对。
  2. 数据迁移:先小表后大表,大表用分批/并行;迁移期间源库只读或双写。
  3. 回归验证:行数、聚合值、抽样字段逐一比对,别只信「跑完了」。
  4. 灰度切换:先读流量切 PG,观察稳定后再切写。

小结

SQL Server → PostgreSQL 的难点集中在方言差异存储过程重写,而非数据本身。把本文的差异表贴在迁移 wiki 上,配合自动化比对,绝大多数坑都能在测试阶段解决,避免在生产炸雷。

上一篇 Linux 服务器磁盘空间爆满:5 步定位与清理实战
下一篇 Vue3 组合式 API + Pinia:状态管理从入门到实战