导出前须确认时间戳字段类型与时区:datetime、timestamp或字符串需区分,MySQL TIMESTAMP按系统时区存储,PostgreSQL timestamptz自动转UTC;字符串时间须解析后再比较,避免字典序错误。
导出前先确认时间戳字段类型和时区
很多导出失败或漏数据,是因为没搞清 created_at、event_time 这类字段到底是 datetime、timestamp 还是字符串(比如 "2024-05-20t14:23:01z")。mysql 的 timestamp 默认按系统时区转存,而 postgresql 的 timestamptz 会自动归一到 utc;如果你用的是日志文件里的字符串时间,得先用 strptime 或数据库的 to_timestamp() 解析,否则 >= '2024-05-20' 这种比较会按字典序错判。
- 查字段类型:MySQL 用
DESCRIBE table_name,PostgreSQL 用\d table_name - 查当前会话时区:MySQL 是
SELECT @@time_zone,PG 是SHOW TIME ZONE - 如果字段是字符串,别直接比 —— 先转成时间类型再过滤,例如 PostgreSQL:
WHERE event_time::timestamptz >= NOW() - INTERVAL '1 day'
SQL 导出时用 WHERE 精确圈定时间范围
“最近一天”不是模糊概念,必须转换成确定的起止时间点。用 NOW() 或 CURRENT_TIMESTAMP 动态计算最稳妥,硬写死 '2024-05-20 00:00:00' 容易跨天失效或漏掉凌晨数据。
- 推荐写法(MySQL):
WHERE created_at >= DATE_SUB(NOW(), INTERVAL 1 DAY) - 更严谨写法(含毫秒、避免边界遗漏):
WHERE created_at >= TIMESTAMPADD(SECOND, -86400, NOW()) - 注意:别用
DATE(created_at) = DATE(NOW()) - 1—— 这会让索引失效,大表导出变慢几倍 - 导出命令里加
--skip-extended-insert(MySQL)或-c "COPY (...)"(PG)能减少解析开销
命令行导出时防止时间被 shell 提前解析
用 mysqldump 或 psql 直接拼 SQL 时,如果把 NOW() - INTERVAL '1 day' 写在 shell 命令里,单引号不牢靠,容易被 shell 当作变量或通配符吃掉,结果导出全表。
- 安全做法:把动态时间逻辑放进 SQL 文件里执行,而不是拼在命令行参数中
- MySQL 示例:
mysqldump -u u -p db table --where="created_at >= DATE_SUB(NOW(), INTERVAL 1 DAY)" > data.sql—— 注意--where值必须用双引号包裹,且内部不能有未转义的 $ 或 ` - PostgreSQL 示例:用
psql -c "COPY (SELECT * FROM events WHERE ts >= NOW() - '1 day'::interval) TO '/tmp/events.csv' WITH CSV HEADER",其中'1 day'::interval显式声明类型,防隐式转换错误
脚本化导出要处理 DST 和跨月边界
单纯减 86400 秒在夏令时切换日会差一小时,比如 3 月 10 日凌晨 2 点跳到 3 点,那一小时的数据就没了。真正“最近 24 小时”得依赖数据库原生时间函数,而不是 shell 的 date -d '1 day ago'。
- Shell 中慎用:
date -d '1 day ago' '+%Y-%m-%d %H:%M:%S'—— 它不感知数据库时区,也不处理闰秒或 DST 跳变 - 更稳方案:让数据库自己算起点,例如 MySQL:
SELECT DATE_SUB(NOW(), INTERVAL 1 DAY),然后把这个结果喂给导出命令 - 如果导出目标是分区表(如按
dt字符串分区),还得额外加条件:AND dt = DATE_FORMAT(DATE_SUB(NOW(), INTERVAL 1 DAY), '%Y-%m-%d'),否则可能扫全分区
实际跑起来才发现,最难的不是写对那条 WHERE,而是确保数据库时区、应用写入时区、导出命令执行时区三者对齐——差一小时,数据就断层。

















