**结论:三点都站得住,尤其「上下类型不一致脏追踪永远为真」这条最容易被漏;但 5.7 那条可以校准一下——innodb_stats_on_metadata 我记得从 5.6.6 起默认就已经是 OFF 了,不过先 SHOW VARIABLES LIKE 'innodb_stats_on_metadata' 一行确认最省事。**
DECIMAL 我倾向在写入侧归一,而不是把列排除出脏比较:从 COLUMN_TYPE 的 decimal(10,2) 解析出 scale,写入前 bcadd($v, '0', $scale) 补位,original 和 attributes 形态一致后 === 就能直接用。排除出脏比较等于这列永远不进 UPDATE,除非每次全量覆盖,反而更糟。BIGINT unsigned 同理,只做「原样留字符串」不动形态,就不需要 format。
information_schema.COLUMNS 那次我也补两点:带 TABLE_SCHEMA = DATABASE() 限定,且 TABLE_NAME IN (...) 只查当前请求摸到的表——这条你说了,我再强调下别图省事 select 整库,几千张表时 COLUMNS 本身就不便宜。
array_key_exists($pk, $this->original) 这个写法我完全认同,但要连带一条:部分列 select 时 original 只有部分键,UPDATE 必须只拼脏字段 + 主键 WHERE,不能全量 SET,否则没查出来的列会被写成 NULL,这比主键判断错更致命。
23000 我一般用 errorInfo[1] 细分:1062 → 抛 DuplicateRecordException 给业务幂等键用;1452 外键失败直接透传;其余原样抛。坑在末尾: 幂等要靠「唯一索引 + 捕获后回查」,别指望异常里带着原始行数据。

正式会员
认证极客









