应优先使用ansi sql标准函数和语法:用cast()替代类型转换函数,case when替代条件函数,current_timestamp替代时间函数,||拼接字符串,标准数据类型如varchar、numeric、date,禁用存储过程复杂逻辑,多库验证边界行为。

用标准SQL函数替代数据库特有函数
不同数据库对字符串、日期、数值处理的内置函数差异极大,比如 SUBSTRING() 在 PostgreSQL 和 SQL Server 中参数顺序不同,而 CONCAT() 在 MySQL 5.0+ 支持,但 Oracle 12c 之前必须用 ||。硬编码这些会导致迁移时大量重写。
实操建议:
- 优先使用 ANSI SQL-92 定义的基础函数:如
CAST()替代CONVERT()或TO_CHAR();用CASE WHEN而非IF()(MySQL)或IIF()(SQL Server) - 避免
GETDATE()(SQL Server)、NOW()(MySQL)、SYSDATE(Oracle),统一用CURRENT_TIMESTAMP - 字符串拼接一律用
||(ANSI 标准),但需确认目标库是否启用标准模式(如 PostgreSQL 默认支持,MySQL 需设置sql_mode=PIPES_AS_CONCAT)
绕开存储过程语法扩展特性
像 DECLARE 变量声明、游标定义、异常处理块(BEGIN TRY / CATCH、EXCEPTION WHEN)在各平台语法和语义都不一致。哪怕同是 PL/pgSQL 和 T-SQL,RETURN 行为、变量作用域也不同。
实操建议:
- 不依赖存储过程控制流逻辑,把复杂分支、循环、错误恢复逻辑下沉到应用层,SQL 层只做原子 DML + 简单条件判断
- 若必须用过程体,只用最简结构:以
BEGIN ... END包裹,内部仅含SELECT/INSERT/UPDATE/DELETE和单层CASE,禁用嵌套块、标签、GOTO - 避免
OUTPUT子句(SQL Server)、RETURNING(PostgreSQL),改用独立SELECT查询刚修改的数据
谨慎处理数据类型与长度声明
VARCHAR(255) 看似通用,但 Oracle 实际按字节计长(VARCHAR2),而 PostgreSQL 按字符;INT 在 SQL Server 是 4 字节,在 PostgreSQL 是别名(实际为 integer),但 MySQL 的 INT 允许带显示宽度如 INT(11)——这在其他库中会被忽略或报错。
实操建议:
- 用标准类型名:
CHAR、VARCHAR、NUMERIC(p,s)、DATE、TIMESTAMP;避开TINYINT、SMALLDATETIME、TEXT等厂商专属类型 - 长度声明只用于
VARCHAR和NUMERIC,且不设“无意义精度”,如不用NUMERIC(10,0)代替INTEGER - 主键/索引字段避免用
UUID类型(PostgreSQL 原生支持,MySQL 需CHAR(36)),统一用CHAR(32)存储十六进制格式
测试阶段必须在多引擎上验证执行计划与边界行为
语法能通过不代表行为一致。例如 ORDER BY 在无 LIMIT 时,SQL Server 可能隐式稳定排序,而 PostgreSQL 不保证;又如空字符串 '' 和 NULL 的等值判断,在 Oracle 中 '' = NULL 为真,其他库全为假。
实操建议:
- 建最小验证集:含空值插入、字符串比较(
WHERE col = '')、分页查询(OFFSET/FETCHvsLIMIT/OFFSET)、聚合空集(AVG(NULL)返回NULL还是报错) - 用 Docker 快速拉起 PostgreSQL、MySQL、SQL Server Express 实例,脚本化执行同一段 SQL 并比对结果行数、字段类型、错误码
- 特别注意事务隔离级别影响:如
READ COMMITTED在 PostgreSQL 是语句级快照,在 SQL Server 是记录级锁,可能导致相同存储过程在并发下返回不同结果
可移植性不是靠“写得像标准”实现的,而是靠主动放弃那些看似方便、实则绑死数据库的语法糖和隐式行为。越早砍掉对特定引擎的依赖,后期迁移成本越低——尤其当 DBA 换人、云厂商锁定、或需要从 SQL Server 迁到开源栈时,这些细节就是卡点。










