Files
junhong_cmp_fiber/migrations/000131_add_iccid_dual_column.up.sql
huang e049080f6c
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 7m47s
分裂iccid长度
2026-04-21 10:55:12 +08:00

98 lines
4.1 KiB
SQL
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
-- 目的:支持上游 IoT 平台传入 19/20 位 ICCID 均能精确命中卡记录
-- 背景:部分运营商 19 位为唯一标识,部分运营商需要完整 20 位才能唯一标识一张卡
-- 策略:双列存储 + 按上游传入长度路由查询Miss 时记录 Warn 日志不降级
-- 注意golang-migrate 默认在事务中运行CREATE INDEX CONCURRENTLY 不能在事务中执行
-- 因此使用 CREATE INDEX IF NOT EXISTS非 CONCURRENTLY
-- 生产环境若需零停机建索引,可手动执行 CONCURRENTLY 版本
-- ==========================================================================
-- 1. tb_iot_card 新增双列
-- ==========================================================================
ALTER TABLE tb_iot_card
ADD COLUMN IF NOT EXISTS iccid_19 varchar(19),
ADD COLUMN IF NOT EXISTS iccid_20 varchar(20);
COMMENT ON COLUMN tb_iot_card.iccid_19 IS 'ICCID前19位所有卡必填用于19位运营商精确查询';
COMMENT ON COLUMN tb_iot_card.iccid_20 IS '完整20位ICCID仅20位运营商卡有值19位卡为NULL';
-- ==========================================================================
-- 1.1a 冲突检测(执行后人工确认均为 0 行,否则停止迁移人工处理)
-- 检测 19 位前缀重复:若 19 位前缀不唯一,回填后 iccid_19 无法作为唯一索引列
-- ==========================================================================
DO $$
DECLARE
conflict_count INTEGER;
abnormal_count INTEGER;
BEGIN
-- 检测 19 位前缀重复
SELECT COUNT(*) INTO conflict_count
FROM (
SELECT LEFT(iccid, 19)
FROM tb_iot_card
WHERE deleted_at IS NULL
GROUP BY LEFT(iccid, 19)
HAVING COUNT(*) > 1
) t;
IF conflict_count > 0 THEN
RAISE EXCEPTION '冲突检测失败:发现 % 组 19 位前缀重复的 ICCID请人工处理后再执行迁移', conflict_count;
END IF;
-- 检测异常长度 ICCID长度既非 19 也非 20 的记录)
SELECT COUNT(*) INTO abnormal_count
FROM tb_iot_card
WHERE deleted_at IS NULL
AND LENGTH(iccid) NOT IN (19, 20);
IF abnormal_count > 0 THEN
RAISE WARNING '警告:发现 % 条异常长度 ICCID 记录长度非19也非20回填后这些卡将无法通过新查询路径命中', abnormal_count;
END IF;
END $$;
-- ==========================================================================
-- 1.2 回填存量数据
-- iccid_19 = LEFT(iccid, 19)(所有卡)
-- iccid_20 = iccid仅当 LENGTH(iccid)=20否则 NULL
-- ==========================================================================
UPDATE tb_iot_card
SET iccid_19 = LEFT(iccid, 19),
iccid_20 = CASE WHEN LENGTH(iccid) = 20 THEN iccid ELSE NULL END;
-- ==========================================================================
-- 1.3 为 iccid_19 创建 Partial Index过滤软删除记录
-- ==========================================================================
CREATE INDEX IF NOT EXISTS idx_iot_card_iccid_19
ON tb_iot_card (iccid_19)
WHERE deleted_at IS NULL;
-- ==========================================================================
-- 1.4 为 iccid_20 创建 Partial Index过滤软删除和 NULL 记录)
-- ==========================================================================
CREATE INDEX IF NOT EXISTS idx_iot_card_iccid_20
ON tb_iot_card (iccid_20)
WHERE deleted_at IS NULL AND iccid_20 IS NOT NULL;
-- ==========================================================================
-- 1.5 tb_personal_customer_iccid 新增 iccid_19 列
-- ==========================================================================
ALTER TABLE tb_personal_customer_iccid
ADD COLUMN IF NOT EXISTS iccid_19 varchar(19);
COMMENT ON COLUMN tb_personal_customer_iccid.iccid_19 IS 'ICCID前19位用于19位运营商精确查询';
-- 回填存量数据
UPDATE tb_personal_customer_iccid
SET iccid_19 = LEFT(iccid, 19);
-- 创建对应 Partial Index
CREATE INDEX IF NOT EXISTS idx_personal_customer_iccid_19
ON tb_personal_customer_iccid (iccid_19)
WHERE deleted_at IS NULL;