此文定制了MySQL 专用 + 商品业务字段 的拉链表全套方案,包含:建表、初始化、每日增量、查询、维护脚本,完全贴合电商商品业务,直接复制到你的 MySQL 即可运行。
一、业务说明(商品场景)
- 表用途:记录商品价格、上下架状态、分类、品牌等变化历史
- 适用:商品信息偶尔变更、需要历史回溯
- 拉链规则:
start_date:记录生效日期(包含)end_date:记录失效日期(不包含)end_date = '9999-12-31'= 当前最新有效数据
- 执行频率:每日凌晨执行一次
二、1. 建表语句(MySQL 正式表)
-- 创建数据仓库库(若无则新建)
CREATE DATABASE IF NOT EXISTS dw;
USE dw;
-- 商品维度拉链表
DROP TABLE IF EXISTS dw_goods_zip;
CREATE TABLE dw_goods_zip (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '自增主键',
goods_id VARCHAR(64) NOT NULL COMMENT '商品ID(业务唯一键)',
goods_name VARCHAR(255) NOT NULL COMMENT '商品名称',
category_id VARCHAR(64) COMMENT '商品分类ID',
category_name VARCHAR(100) COMMENT '商品分类名称',
brand_id VARCHAR(64) COMMENT '品牌ID',
brand_name VARCHAR(100) COMMENT '品牌名称',
sale_price DECIMAL(18,2) COMMENT '销售价',
market_price DECIMAL(18,2) COMMENT '市场价',
goods_status TINYINT COMMENT '商品状态 1=上架 2=下架 3=删除',
stock_num INT COMMENT '库存数量',
start_date DATE NOT NULL COMMENT '生效日期',
end_date DATE NOT NULL COMMENT '失效日期',
is_current TINYINT NOT NULL COMMENT '1=当前有效 0=已失效',
create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '入库时间',
PRIMARY KEY (id),
UNIQUE KEY uk_goods_start (goods_id, start_date),
KEY idx_is_current (is_current),
KEY idx_date_range (start_date, end_date)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='商品维度拉链表';
三、2. 首次全量初始化脚本(基线数据)
USE dw;
-- 清空历史(首次执行)
TRUNCATE TABLE dw_goods_zip;
-- 全量导入商品基线数据
-- 源表请替换成你的业务商品表:例如 ods.ods_goods_full
INSERT INTO dw_goods_zip (
goods_id, goods_name, category_id, category_name,
brand_id, brand_name, sale_price, market_price,
goods_status, stock_num, start_date, end_date, is_current
)
SELECT
goods_id,
goods_name,
category_id,
category_name,
brand_id,
brand_name,
sale_price,
market_price,
goods_status,
stock_num,
'2026-01-01' AS start_date, -- 统一基线日期
'9999-12-31' AS end_date, -- 永久有效
1 AS is_current
FROM ods.ods_goods_full; -- 你的业务商品全量表
四、3. 每日增量更新脚本(核心 ⭐ 直接定时执行)
使用说明
-
@biz_dt:业务日期 = 前一天(例如今天 4.02,biz_dt = 4.01) -
脚本作用:关闭旧记录 + 插入新记录
-
可直接放在定时任务 / 调度平台执行
USE dw;
-- ================= 变量配置 =================
SET @biz_dt = '2026-04-01'; -- 前一天日期(每日修改)
SET @new_start_date = @biz_dt + 1; -- 新记录生效日 = T日
SET @current_end = '9999-12-31'; -- 永久有效-- 1. 创建当日变更增量临时表
DROP TEMPORARY TABLE IF EXISTS tmp_goods_delta;
CREATE TEMPORARY TABLE tmp_goods_delta (
goods_id VARCHAR(64) PRIMARY KEY,
goods_name VARCHAR(255),
category_id VARCHAR(64),
category_name VARCHAR(100),
brand_id VARCHAR(64),
brand_name VARCHAR(100),
sale_price DECIMAL(18,2),
market_price DECIMAL(18,2),
goods_status TINYINT,
stock_num INT
);-- 2. 插入【前一天变更/新增】的商品数据
INSERT INTO tmp_goods_delta
SELECT
goods_id, goods_name, category_id, category_name,
brand_id, brand_name, sale_price, market_price,
goods_status, stock_num
FROM ods.ods_goods_inc
WHERE dt = @biz_dt; -- 增量表按日期分区-- 3. 开启事务(保证数据原子性)
START TRANSACTION;-- 4. 关闭旧的有效记录(更新失效日期)
UPDATE dw_goods_zip
SET end_date = @biz_dt,
is_current = 0
WHERE is_current = 1
AND goods_id IN (SELECT goods_id FROM tmp_goods_delta);-- 5. 插入新的有效记录
INSERT INTO dw_goods_zip (
goods_id, goods_name, category_id, category_name,
brand_id, brand_name, sale_price, market_price,
goods_status, stock_num, start_date, end_date, is_current
)
SELECT
goods_id, goods_name, category_id, category_name,
brand_id, brand_name, sale_price, market_price,
goods_status, stock_num,
@new_start_date,
@current_end,
1
FROM tmp_goods_delta;-- 6. 提交事务
COMMIT;
五、4. 最常用查询脚本(直接复制使用)
① 查询当前所有有效商品(最新状态)
SELECT * FROM dw_goods_zip WHERE is_current = 1;
② 回溯历史:查询某一天的商品快照
SELECT *
FROM dw_goods_zip
WHERE start_date <= '2026-03-15'
AND end_date > '2026-03-15';
③ 查看单个商品完整变更历史
SELECT *
FROM dw_goods_zip
WHERE goods_id = 'G20260001'
ORDER BY start_date;
④ 统计商品变更次数
SELECT goods_id, goods_name, COUNT(*) AS change_times
FROM dw_goods_zip
GROUP BY goods_id, goods_name
ORDER BY change_times DESC;
六、5. MySQL 拉链表维护规范(必看)
- 禁止删除 / 修改历史记录 只允许:新增记录 + 更新
end_date关闭旧记录 - 索引已建好保证历史查询速度,无需额外加索引
- 数据膨胀处理 超过 1 年的失效数据可归档到
dw_goods_zip_hist - 事务必须开启MySQL 下用事务保证拉链不丢数据、不乱序
- 源表要求 业务库必须有:
update_time或dt日期分区,用于抽取增量
MySQL 商品拉链表【存储过程终极版】(某商城生产环境)
全自动、传参即用、事务兜底、适配商品业务,可直接定时调用。
一、前置基础表(复用之前标准商品拉链表)
CREATE DATABASE IF NOT EXISTS dw;
USE dw;
DROP TABLE IF EXISTS dw_goods_zip;
CREATE TABLE dw_goods_zip (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '自增主键',
goods_id VARCHAR(64) NOT NULL COMMENT '商品ID',
goods_name VARCHAR(255) NOT NULL COMMENT '商品名称',
category_id VARCHAR(64) COMMENT '分类ID',
category_name VARCHAR(100) COMMENT '分类名称',
brand_id VARCHAR(64) COMMENT '品牌ID',
brand_name VARCHAR(100) COMMENT '品牌名称',
sale_price DECIMAL(18,2) COMMENT '售价',
market_price DECIMAL(18,2) COMMENT '市场价',
goods_status TINYINT COMMENT '1上架2下架3删除',
stock_num INT COMMENT '库存',
start_date DATE NOT NULL COMMENT '生效日',
end_date DATE NOT NULL COMMENT '失效日',
is_current TINYINT NOT NULL COMMENT '1当前有效0历史',
create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '入仓时间',
PRIMARY KEY (id),
UNIQUE KEY uk_goods_start (goods_id,start_date),
KEY idx_current (is_current),
KEY idx_dt_range (start_date,end_date)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='商品信息拉链表';
二、创建全自动拉链存储过程
逻辑:传入业务日期→自动取增量→关闭旧数据→插新数据→事务保证原子性
USE dw;
DROP PROCEDURE IF EXISTS p_goods_zip_refresh;
DELIMITER //
CREATE PROCEDURE p_goods_zip_refresh(IN p_biz_dt DATE)
BEGIN
-- 定义变量
DECLARE v_new_start DATE;
DECLARE v_max_end DATE DEFAULT '9999-12-31';
-- 新记录生效日 = 业务日+1
SET v_new_start = DATE_ADD(p_biz_dt,INTERVAL 1 DAY);
-- 临时增量表(会话级临时表)
DROP TEMPORARY TABLE IF EXISTS tmp_goods_delta;
CREATE TEMPORARY TABLE tmp_goods_delta (
goods_id VARCHAR(64) PRIMARY KEY,
goods_name VARCHAR(255),
category_id VARCHAR(64),
category_name VARCHAR(100),
brand_id VARCHAR(64),
brand_name VARCHAR(100),
sale_price DECIMAL(18,2),
market_price DECIMAL(18,2),
goods_status TINYINT,
stock_num INT
) ENGINE=InnoDB;
-- 1. 加载当日变更增量(替换成你真实增量表 ods.ods_goods_inc)
INSERT INTO tmp_goods_delta
SELECT goods_id,goods_name,category_id,category_name,
brand_id,brand_name,sale_price,market_price,
goods_status,stock_num
FROM ods.ods_goods_inc
WHERE dt = p_biz_dt;
-- 无增量直接退出
IF NOT EXISTS (SELECT 1 FROM tmp_goods_delta) THEN
SELECT CONCAT(p_biz_dt,' 无商品变更数据,流程结束') AS result;
LEAVE proc_end;
END IF;
-- 2. 事务执行拉链闭环
START TRANSACTION;
-- 关闭旧有效记录:失效日改为业务日,置为历史
UPDATE dw_goods_zip
SET end_date = p_biz_dt, is_current = 0
WHERE is_current = 1
AND goods_id IN (SELECT goods_id FROM tmp_goods_delta);
-- 插入新版当前有效记录
INSERT INTO dw_goods_zip
(goods_id,goods_name,category_id,category_name,
brand_id,brand_name,sale_price,market_price,
goods_status,stock_num,start_date,end_date,is_current)
SELECT goods_id,goods_name,category_id,category_name,
brand_id,brand_name,sale_price,market_price,
goods_status,stock_num,v_new_start,v_max_end,1
FROM tmp_goods_delta;
COMMIT;
SELECT CONCAT('执行成功!业务日期:',p_biz_dt,' 新增生效日:',v_new_start) AS result;
proc_end:
DROP TEMPORARY TABLE IF EXISTS tmp_goods_delta;
END //
DELIMITER ;
三、首次全量初始化存储过程(基线打底)
USE dw;
DROP PROCEDURE IF EXISTS p_goods_zip_init;
DELIMITER //
CREATE PROCEDURE p_goods_zip_init(IN p_start_dt DATE)
BEGIN
TRUNCATE TABLE dw_goods_zip;
INSERT INTO dw_goods_zip
(goods_id,goods_name,category_id,category_name,
brand_id,brand_name,sale_price,market_price,
goods_status,stock_num,start_date,end_date,is_current)
SELECT goods_id,goods_name,category_id,category_name,
brand_id,brand_name,sale_price,market_price,
goods_status,stock_num,p_start_dt,'9999-12-31',1
FROM ods.ods_goods_full; -- 你的商品全量基线表
SELECT CONCAT('初始化完成,基线起始日期:',p_start_dt) AS init_result;
END //
DELIMITER ;
四、调用方式(超简单)
1、第一次打底初始化
-- 统一基线开始日期,比如2026-01-01
CALL dw.p_goods_zip_init('2026-01-01');
2、每日定时调度(只传前一天日期)
-- 例:跑4月2日任务,传4月1日
CALL dw.p_goods_zip_refresh('2026-04-01');
五、常用查询(直接配套使用)
-- 1.查当前最新商品全量
SELECT * FROM dw_goods_zip WHERE is_current=1;
-- 2.回溯某天快照
SELECT * FROM dw_goods_zip
WHERE start_date <= '2026-03-20' AND end_date > '2026-03-20';
-- 3.单商品全变更轨迹
SELECT * FROM dw_goods_zip WHERE goods_id='G001' ORDER BY start_date;
六、运维 & 定时建议
- 定时任务 :MySQL 事件 / CRON / 调度平台,每日凌晨执行
CALL p_goods_zip_refresh(前一日日期) - 严格禁止手动 update/delete 历史拉链数据
- 长期数据可归档:把 end_date < 'xxxx-xx-xx' 的历史记录迁移到归档表
- 所有增量依赖
ods.ods_goods_inc按 dt 分区,确保能抓到每日变更