SQL 如何用 RETURNING / OUTPUT 返回更新后的所有字段

9次阅读

可以,postgresql的RETURNING支持但仅限单表更新;多表需写main_table.;SQL Server用OUTPUT INSERTED.或deleteD.;mysql 8.0.23+和sqlite 3.35.0+部分支持,旧版需select+UPDATE两步。

SQL 如何用 RETURNING / OUTPUT 返回更新后的所有字段

PostgreSQL 的 RETURNING 能否返回所有字段?

可以,但必须显式列出字段名或用 *(仅限单表更新)。UPDATE ... RETURNING * 是最简写法,前提是没涉及 JOIN 或子查询——否则 PostgreSQL 会报错 Error: cannot return from a relation that is not the target of the INSERT/UPDATE/DELETE

常见错误是以为 RETURNING * 在多表 UPDATE 中也生效,其实它只认主表。如果用了 FROM 子句关联其他表,就得手动写 RETURNING table_name.*,否则报错。

  • 单表更新:直接用 RETURNING *
  • FROM 的更新(如关联查条件):必须写 RETURNING main_table.*
  • 想返回计算字段或别名?得单独列出来,* 不包含它们

SQL Server 的 OUTPUT 怎么返回整行?

OUTPUT 不支持 *,必须明确指定列。最接近“所有字段”的写法是 OUTPUT INSERTED.*OUTPUT DELETED.*,取决于你想要更新前还是更新后的值。

注意:如果表有计算列、稀疏列或托管类型(如 geography),INSERTED.* 仍会返回它们,但某些客户端驱动可能解析失败——建议在复杂表结构中优先列出关键业务字段。

  • 更新后整行:用 OUTPUT INSERTED.*
  • 更新前整行:用 OUTPUT DELETED.*
  • 混合返回?可以,比如 OUTPUT INSERTED.id, DELETED.status, GETDATE()
  • 不能写 OUTPUT *,语法直接报错 Incorrect syntax near '*'

MySQL 和 SQLite 没有原生 RETURNING,怎么绕过?

MySQL 8.0.23+ 支持 RETURNING,但仅限单表、且不支持 *;必须写全字段或用生成列别名。SQLite 3.35.0+ 也加了 RETURNING,行为类似 PostgreSQL,支持 *,但仅限无 JOIN 的 UPDATE。

老版本 MySQL/SQLite 用户常用两步法:先 SELECT 出待更新的行(带 WHERE 条件),再执行 UPDATE,靠应用层拼接结果。缺点是并发下可能被其他事务改掉——如果业务允许轻微不一致,这是最稳的 fallback 方案。

  • MySQL 8.0.23+:支持 UPDATE ... RETURNING col1, col2,不支持 RETURNING *
  • SQLite 3.35.0+:支持 RETURNING *,但要求语句里没 JOIN、没子查询
  • 低版本通用方案:用 SELECT ... for UPDATE(InnoDB)或 BEGIN IMMEDIATE(SQLite)加事务锁住数据

数据库写法兼容性要注意什么?

没有银弹。RETURNING / OUTPUT 的语法、语义、甚至事务可见性都不同。比如 PostgreSQL 的 RETURNING 在语句提交前就返回结果,而 SQL Server 的 OUTPUT 在语句执行中就输出,可能被触发器干扰。

更隐蔽的问题是 NULL 处理:OUTPUT INSERTED.col 在该列为 NULL 时照常返回 NULL,但某些 ORM 会把未返回的列默认设为空字符串或 0——这容易掩盖字段是否真被更新。

  • 别依赖 * 跨库移植,字段增减会导致查询失败或静默截断
  • 批量更新时,PostgreSQL 的 RETURNING 返回全部匹配行,SQL Server 的 OUTPUT 也是,但 MySQL 的 RETURNINGROW_COUNT() 影响,可能少于预期
  • 如果用在存储过程里,SQL Server 的 OUTPUT 可插入临时表,PostgreSQL 的 RETURNING 可直接作为 CTE 输入,能力不对等
text=ZqhQzanResources