大约 5 分钟
以下是SQL Server 专属函数-替代方案对照表,按功能分类整理,包含用途说明、SQL Server 示例及 PostgreSQL/MySQL 的替代写法,迁移时可直接参考:
| 函数分类 | SQL Server 函数 | 用途说明 | SQL Server 示例 | PostgreSQL 替代方案 | MySQL 替代方案 |
|---|---|---|---|---|---|
| XML 处理 | nodes() | 将 XML 节点拆解为关系型行集(行转列核心函数) | SELECT T.c.query('.') FROM tab CROSS APPLY xml_col.nodes('/root/node') T(c) | SELECT x.* FROM tab, xmltable('/root/node' passing xml_col columns val xml path '.') x(需 PostgreSQL 9.3+) | 无直接函数,需用 ExtractValue(xml_col, '/root/node[$i]/text()') 结合循环(需手动控制索引 $i) |
query() | 执行 XQuery 表达式查询 XML 片段 | SELECT xml_col.query('/root/node[@id=1]') FROM tab | SELECT (xpath('/root/node[@id=1]', xml_col))[1]::xml(xpath 函数返回数组,取第一个元素) | SELECT ExtractValue(xml_col, '/root/node[@id=1]')(仅支持简单路径,不支持复杂XQuery) | |
value() | 从 XML 节点提取指定类型的值 | SELECT xml_col.value('(/root/node/text())[1]', 'int') FROM tab | SELECT (xpath('/root/node/text()', xml_col))[1]::int(数组转指定类型) | SELECT ExtractValue(xml_col, '/root/node[1]/text()') + 0(通过运算强制类型转换) | |
modify() | 直接修改 XML 节点(插入/更新/删除) | UPDATE tab SET xml_col.modify('replace value of (/root/node/text())[1] with "new"') | 无直接函数,需用字符串函数拼接新XML:UPDATE tab SET xml_col = regexp_replace(xml_col::text, '<node>.*?</node>', '<node>new</node>')::xml | 无直接函数,用 REPLACE(xml_col, '旧值', '新值') 字符串替换(仅限简单场景) | |
exist() | 判断 XML 中是否存在符合条件的节点(返回 1/0) | SELECT CASE WHEN xml_col.exist('/root/node') = 1 THEN '存在' ELSE '不存在' END | SELECT CASE WHEN xpath_exists('/root/node', xml_col) THEN '存在' ELSE '不存在' END(PostgreSQL 10+) | SELECT CASE WHEN ExtractValue(xml_col, 'count(/root/node)') > 0 THEN '存在' ELSE '不存在' END | |
| 聚合/窗口函数 | STRING_AGG(expr, sep) | 按分组聚合字符串(参数:表达式+分隔符) | SELECT dept, STRING_AGG(emp_name, ',') FROM emp GROUP BY dept | SELECT dept, string_agg(emp_name, ',') FROM emp GROUP BY dept(函数名小写,参数顺序相同) | SELECT dept, GROUP_CONCAT(emp_name SEPARATOR ',') FROM emp GROUP BY dept(默认分隔符为逗号,需指定SEPARATOR) |
FIRST_VALUE(expr) OVER(ORDER BY col ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW IGNORE NULLS) | 取窗口内首个非空值(带 IGNORE NULLS) | SELECT FIRST_VALUE(score IGNORE NULLS) OVER(ORDER BY id) FROM stu | SELECT FIRST_VALUE(score) OVER(ORDER BY id ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) FROM stu(PostgreSQL 13+ 支持 IGNORE NULLS,低版本需用 CASE 过滤) | SELECT FIRST_VALUE(score) OVER(ORDER BY id ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) FROM stu(MySQL 8.0+ 支持,但不直接支持 IGNORE NULLS,需用 CASE WHEN score IS NOT NULL THEN score END 预处理) | |
| 安全/权限 | SUSER_SNAME(login_id) | 通过登录ID获取登录名(服务器级用户) | SELECT SUSER_SNAME(101) AS login_name | SELECT rolname FROM pg_authid WHERE oid = 101(pg_authid 存储登录角色信息) | SELECT user FROM mysql.user WHERE Host = 'localhost' AND user_id = 101(需结合主机名过滤) |
OBJECTPROPERTY(obj_id, 'IsView') | 判断对象是否为视图(返回 1/0) | SELECT OBJECTPROPERTY(OBJECT_ID('dbo.v1'), 'IsView') | SELECT CASE WHEN relkind = 'v' THEN 1 ELSE 0 END FROM pg_class WHERE relname = 'v1' AND relnamespace = (SELECT oid FROM pg_namespace WHERE nspname = 'dbo')(relkind='v' 表示视图) | SELECT CASE WHEN table_type = 'VIEW' THEN 1 ELSE 0 END FROM information_schema.tables WHERE table_name = 'v1' AND table_schema = 'dbo' | |
| 元数据查询 | COL_LENGTH('table', 'col') | 获取列的长度(如 varchar(50) 返回 50) | SELECT COL_LENGTH('emp', 'name') | SELECT character_maximum_length FROM information_schema.columns WHERE table_name = 'emp' AND column_name = 'name' AND table_schema = 'dbo' | SELECT character_maximum_length FROM information_schema.columns WHERE table_name = 'emp' AND column_name = 'name' AND table_schema = 'dbo' |
OBJECT_ID('schema.obj') | 获取对象的唯一ID(如表、视图) | SELECT OBJECT_ID('dbo.emp') AS obj_id | SELECT oid FROM pg_class WHERE relname = 'emp' AND relnamespace = (SELECT oid FROM pg_namespace WHERE nspname = 'dbo')(oid 为PostgreSQL对象唯一ID) | SELECT table_id FROM information_schema.tables WHERE table_name = 'emp' AND table_schema = 'dbo'(MySQL 8.0+ 支持 table_id) | |
| 日期时间 | DATEADD(interval, num, date) | 日期加减(支持 year/month/day/hour 等单位) | SELECT DATEADD(day, 3, '2023-01-01')(加3天) | SELECT '2023-01-01'::date + interval '3 days'(用 interval 关键字,单位为复数) | SELECT DATE_ADD('2023-01-01', INTERVAL 3 DAY)(单位为单数,参数顺序:日期+间隔) |
DATEDIFF(interval, start, end) | 计算两个日期的差值(返回整数) | SELECT DATEDIFF(month, '2023-01-01', '2023-03-01')(返回2) | SELECT EXTRACT(year FROM age('2023-03-01'::date, '2023-01-01'::date)) * 12 + EXTRACT(month FROM age('2023-03-01'::date, '2023-01-01'::date))(通过 age 函数拆解年月) | SELECT TIMESTAMPDIFF(MONTH, '2023-01-01', '2023-03-01')(参数顺序:单位+开始+结束) | |
| 其他工具函数 | PARSENAME('obj', level) | 按点拆分对象名(如 'a.b.c.d' 拆分为4部分,level=1取最后一部分) | SELECT PARSENAME('dbo.emp.name', 2)(返回 'emp') | SELECT split_part('dbo.emp.name', '.', 2)(split_part 按分隔符拆分,索引从1开始) | SELECT SUBSTRING_INDEX(SUBSTRING_INDEX('dbo.emp.name', '.', 2), '.', -1)(先取前2部分,再取最后1部分) |
FORMATMESSAGE('模板 %d %s', 1, 'a') | 格式化字符串(类似 sprintf) | SELECT FORMATMESSAGE('ID: %d, Name: %s', 10, 'Tom')(返回 'ID: 10, Name: Tom') | SELECT format('ID: %s, Name: %s', 10, 'Tom')(format 函数,占位符统一为 %s,自动转换类型) | SELECT CONCAT('ID: ', 10, ', Name: ', 'Tom')(用 CONCAT 拼接,或 FORMAT(10, 'ID: %d, Name: Tom') 有限支持) | |
NULLIFZERO(num) | 若数值为0则返回NULL,否则返回原数值 | SELECT NULLIFZERO(0) (返回NULL)、SELECT NULLIFZERO(5)(返回5) | SELECT CASE WHEN 0 = 0 THEN NULL ELSE 0 END、SELECT CASE WHEN 5 = 0 THEN NULL ELSE 5 END | SELECT CASE WHEN 0 = 0 THEN NULL ELSE 0 END、SELECT CASE WHEN 5 = 0 THEN NULL ELSE 5 END |
关键说明:
- 版本差异:部分替代函数依赖数据库版本(如 PostgreSQL 的
xpath_exists需 10+,MySQL 的窗口函数需 8.0+),迁移前需确认目标库版本。 - XML 功能限制:PostgreSQL 的 XML 处理依赖
xpath系列函数,功能弱于 SQL Server;MySQL 的 XML 支持更基础,复杂场景建议先将 XML 解析为关系表。 - 元数据查询:PostgreSQL 依赖
pg_*系统表(如pg_class、pg_namespace),MySQL 依赖INFORMATION_SCHEMA,需注意架构(schema)的映射关系(SQL Server 的dbo通常对应 PostgreSQL/MySQL 的public或自定义 schema)。
如果需要某类函数的更复杂场景示例,可以进一步补充说明!
