70 lines
3.1 KiB
SQL
70 lines
3.1 KiB
SQL
-- 006_accommodation_history_constraint.sql
|
||
-- 目标:移除 train_accommodation 全局唯一约束 uq_accommodation_enroll,
|
||
-- 创建部分唯一索引 uq_accommodation_enroll_active (每个 enrollment 最多 1 条有效住宿),
|
||
-- 允许学员拥有多条退宿历史记录 (status=2),彻底支持调房与历史留痕。
|
||
|
||
DO $$
|
||
DECLARE
|
||
v_dup_count INTEGER;
|
||
v_con_def TEXT;
|
||
v_idx_def TEXT;
|
||
BEGIN
|
||
-- 1. Pre-Migration Data Safety Guard
|
||
-- 检查是否已存在同一个 enrollment_id 拥有 2 条或以上处于有效状态 (0=预分配, 1=已入住) 的住宿记录
|
||
SELECT COUNT(*) INTO v_dup_count
|
||
FROM (
|
||
SELECT enrollment_id
|
||
FROM train_accommodation
|
||
WHERE accommodation_status IN (0, 1)
|
||
GROUP BY enrollment_id
|
||
HAVING COUNT(*) > 1
|
||
) t;
|
||
|
||
IF v_dup_count > 0 THEN
|
||
RAISE EXCEPTION 'Pre-migration check failed: found % enrollment(s) with multiple active accommodations (status IN (0,1)). Manual resolution required before applying migration 006.', v_dup_count;
|
||
END IF;
|
||
|
||
-- 2. Existing Constraint Guard (Idempotent Drop)
|
||
-- 检查约束 uq_accommodation_enroll 是否存在且符合预期
|
||
SELECT pg_get_constraintdef(c.oid) INTO v_con_def
|
||
FROM pg_constraint c
|
||
JOIN pg_class t ON c.conrelid = t.oid
|
||
WHERE t.relname = 'train_accommodation'
|
||
AND c.conname = 'uq_accommodation_enroll';
|
||
|
||
IF v_con_def IS NOT NULL THEN
|
||
-- 验证旧约束是否为 UNIQUE (enrollment_id)
|
||
IF v_con_def ILIKE 'UNIQUE (enrollment_id)%' THEN
|
||
RAISE NOTICE 'Dropping legacy global unique constraint: uq_accommodation_enroll';
|
||
ALTER TABLE train_accommodation DROP CONSTRAINT uq_accommodation_enroll;
|
||
ELSE
|
||
RAISE EXCEPTION 'Constraint collision: constraint uq_accommodation_enroll exists but definition is unexpected: %', v_con_def;
|
||
END IF;
|
||
ELSE
|
||
RAISE NOTICE 'Constraint uq_accommodation_enroll does not exist, skipping drop.';
|
||
END IF;
|
||
|
||
-- 3. New Index Collision Guard & Creation
|
||
-- 检查部分唯一索引 uq_accommodation_enroll_active
|
||
SELECT pg_get_indexdef(i.indexrelid) INTO v_idx_def
|
||
FROM pg_index i
|
||
JOIN pg_class c ON i.indexrelid = c.oid
|
||
JOIN pg_class t ON i.indrelid = t.oid
|
||
WHERE t.relname = 'train_accommodation'
|
||
AND c.relname = 'uq_accommodation_enroll_active';
|
||
|
||
IF v_idx_def IS NOT NULL THEN
|
||
-- 验证已有索引是否为预期的部分唯一索引
|
||
IF v_idx_def ILIKE '%UNIQUE INDEX uq_accommodation_enroll_active ON %train_accommodation%enrollment_id%WHERE%accommodation_status%' THEN
|
||
RAISE NOTICE 'Partial unique index uq_accommodation_enroll_active already exists with expected definition, skipping creation.';
|
||
ELSE
|
||
RAISE EXCEPTION 'Index collision: index uq_accommodation_enroll_active exists but definition differs: %', v_idx_def;
|
||
END IF;
|
||
ELSE
|
||
RAISE NOTICE 'Creating partial unique index: uq_accommodation_enroll_active';
|
||
CREATE UNIQUE INDEX uq_accommodation_enroll_active
|
||
ON train_accommodation (enrollment_id)
|
||
WHERE accommodation_status IN (0, 1);
|
||
END IF;
|
||
END $$;
|