根本原因是时区、SQL mode与同步策略未对齐:MySQL 8.0开启explicit_defaults_for_timestamp后,NULL值被严格拒绝或静默转NULL;Navicat以客户端时区发送INSERT,而目标库时区或SQL mode(如STRICT_TRANS_TABLES)不匹配,导致时间错乱或变NULL。
navicat 数据同步中时间戳字段变 null 或值错乱,根本原因不是数据丢了,而是时区、sql mode 和同步策略三者没对齐——尤其在 mysql 8.0+ 环境下,explicit_defaults_for_timestamp 开启后,空值会被严格拒绝或静默转成 null。
为什么 NOW() 和 CURRENT_TIMESTAMP 在同步后失效?
源库用 NOW() 插入的时间是 CST(东八区),目标库连接时区设为 UTC,Navicat 默认以客户端时区发 INSERT,结果存进数据库的是 UTC 时间,但显示时又被本地客户端按 CST 解析,看起来“偏了 8 小时”。更糟的是:如果目标表字段定义是 TIMESTAMP DEFAULT CURRENT_TIMESTAMP 但没加 NOT NULL,而 Navicat 同步时传了 NULL(比如字段映射漏配、源值为空),MySQL 8.0 在 STRICT 模式下会直接拒写,日志里只报 “1 warning”,不报错也不中断任务。
- 检查目标库实际时区:
SELECT @@time_zone, @@system_time_zone; - 确认 SQL mode 是否含
STRICT_TRANS_TABLES:SELECT @@sql_mode; - 别依赖 Navicat 预览窗口的时间显示——它受本地系统时区影响,用
SELECT created_at, UNIX_TIMESTAMP(created_at) FROM your_table LIMIT 1;看真实秒数更准
如何让 Navicat 同步时正确填充时间戳字段?
关键不是改代码,而是控制 Navicat 发送的值。默认它会把源字段原样传过去,一旦源是 NULL 或空字符串,目标就收不到有效时间。
- 在数据同步向导「高级」页,务必勾选
设置时间戳字段为 CURRENT_TIMESTAMP——这个选项会让 Navicat 把所有空/无效的时间戳字段替换成CURRENT_TIMESTAMP函数调用,而不是传 NULL - 如果目标表字段允许 NULL,建议先执行 DDL 改成强制非空:
ALTER TABLE logs MODIFY created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP; - 避免用
NOW()做默认值;统一用CURRENT_TIMESTAMP,MySQL 对它的解析行为更稳定
PostgreSQL 连接里时间戳总对不上怎么办?
PostgreSQL 不像 MySQL 那样有全局 time_zone 变量,每个连接 session 的 timezone 是独立的。Navicat 每次新建查询窗口就是新 session,手动 SET timezone = 'Asia/Shanghai'; 只管当前窗口,一关就丢。
- 正确做法:进连接属性 → 「高级」页 → 在
Initial statement输入框里填:SET timezone = 'Asia/Shanghai';(注意结尾分号) - 验证是否生效:同步前开个新查询窗口,直接运行
SHOW timezone;和SELECT NOW();,对比输出是否符合预期 - 如果目标库是云服务(如 AWS RDS),确认参数组里
timezone参数已设为Asia/Shanghai,否则 Initial statement 可能被覆盖
真正麻烦的从来不是某个时间差几小时,而是错误分散在时区配置、SQL mode、字段定义、Navicat 同步选项四个地方,且任一环节没对齐,都会导致时间字段在预览里看着正常,执行后却 NULL 或错位——查问题时得同时打开四个终端分别验证,缺一不可。


















