← 返回题目列表

生效时间和失效时间字段如何设计?如何查询当前有效数据?

中等 第 27 / 33 题 更新于 2026/07/30
生效时间版本数据时态设计

简化版

有版本生效语义的数据通常设计 effective_starteffective_end,表示某条配置或价格在某个时间区间有效。查询当前有效数据时用 start <= nowend > now,并通过唯一约束或业务校验避免同一对象的有效期重叠。

详细版

价格、合同、费率、权益规则、配置策略都可能不是“立即覆盖”,而是从未来某个时间开始生效。如果只保留一条当前记录,就无法表达预约生效和历史追溯。

常见做法是每次变更新增一条版本记录,记录生效起止时间。effective_end 可以用一个远未来时间表示仍有效,也可以允许为空,但查询和索引要统一。

关键难点是防止时间区间重叠。例如同一个商品在 8 月 1 日到 8 月 10 日不能有两条不同价格同时有效。

完整版教学

一、为什么不能只覆盖当前值

很多业务数据有“什么时候开始算数”的问题。商品促销价下周一生效,会员权益下个月变更,合同费率按签约日期执行。

如果只在主表里保存一个当前值,每次修改都覆盖旧值,就会丢失历史,也无法预约未来变更。

生效时间设计的价值是把数据从“一个点”变成“一段时间内有效的版本”。

二、典型表结构怎么设计

可以为价格、规则、合同条款建版本表:

CREATE TABLE product_price_version (
  id BIGINT PRIMARY KEY,
  product_id BIGINT NOT NULL,
  price DECIMAL(18,2) NOT NULL,
  effective_start DATETIME NOT NULL,
  effective_end DATETIME NOT NULL,
  status VARCHAR(20) NOT NULL,
  KEY idx_product_time (product_id, effective_start, effective_end)
);

查询当前有效价格:

SELECT *
FROM product_price_version
WHERE product_id = ?
  AND effective_start <= NOW()
  AND effective_end > NOW()
  AND status = 'ACTIVE';

这里推荐左闭右开区间 [start, end),可以避免两个版本在边界点重复命中。

三、为什么要防止区间重叠

同一个对象同一时刻只能有一个有效版本。否则查询当前价格时可能返回两条,业务不知道用哪一条。

例如商品 1001 有两个版本:

版本A:2026-08-01 00:00 ~ 2026-08-10 00:00,99 元
版本B:2026-08-05 00:00 ~ 2026-08-15 00:00,89 元

8 月 6 日两条都有效,这就是重叠。多数数据库不能简单用普通唯一索引表达“区间不重叠”,需要应用校验、事务锁或数据库特定约束。

四、effective_end 用 NULL 还是远未来时间

NULL 表示无结束时间,语义清晰;远未来时间如 9999-12-31 查询更简单。两种都能用,关键是全系统统一。

方案优点缺点
end IS NULL语义自然查询条件多一个分支
9999-12-31范围查询统一魔法值要规范
单独 current 标记查询快容易和时间区间不一致

如果使用 current_flag,也要把它当冗余索引字段维护,不能替代时间区间。

五、变更流程要可审计

有生效时间的数据通常涉及合同、价格、权益,不能随便 update。更稳的做法是新增版本、关闭旧版本、保留操作人和审批记录。

当未来版本还没生效时,允许撤销或修改;一旦已经生效并被订单引用,就要谨慎处理,通常通过新版本修正,而不是直接改历史版本。

这和快照设计配合使用:订单记录成交时刻的价格快照,价格版本表负责解释当时价格来源。

六、常见误区与追问

  • 误区:生效时间只要一个 start 字段就够。 没有 end 很难表达历史区间和当前查询边界。
  • 误区:用最大 id 就能代表当前版本。 最大 id 不一定已经生效,也不一定没被撤销。
  • 误区:时间区间重叠无所谓。 重叠会导致同一时刻多条有效记录,业务语义冲突。
  • 追问:为什么推荐左闭右开? 相邻版本 end = next_start 时不会在边界重复命中。
  • 追问:如何避免重叠? 用事务内范围检查、对象维度锁、排他约束或审批发布流程控制。

七、加强记忆

记忆钩子:生效时间设计不是记录“改过什么”,而是给规则画时间轴;任何时刻只能有一条线覆盖当前点。

回答这题要讲“版本表、左闭右开、当前查询、防重叠、历史审计”。它本质是时态数据建模,不是简单多两个时间字段。