-- ============================================================================ -- Merge du an trung do khac DAU-CACH trong Code (giu ban KHONG-dau-cach). -- "FLOCK 01" -> "FLOCK01" | "FLOCK 03" -> "FLOCK03" | "CAL 01" -> "CAL01" -- -- AN-TOAN / IDEMPOTENT: -- * Survivor = ban no-space (co trong seed => restart-durable). Retire ban space. -- * Moi cap chay trong 1 TRANSACTION rieng; VERIFY 0 live-ref TRUOC khi an mem; -- lech -> ROLLBACK cap do + in [ABORT], KHONG an mem. -- * >1 ban song cung code (reseed cycle) -> [ABORT], can nguoi reconcile 1 GUID. -- * Chay lai an-toan: cap da gop -> [SKIP] (khong con ban space song). -- * PeWorkItemBudgets co UNIQUE (ProjectId,WorkItemId) filter IsDeleted=0: -- repoint neu survivor CHUA co budget live cung WorkItem; nguoc lai (collision) -- -> soft-delete ban space (giu survivor). KHONG xoa cung. DeletedBy = NULL. -- * Refs la loose-Guid (KHONG co physical FK) -> khong vo thu tu; nhung DB KHONG -- co luoi an toan => VERIFY la bat buoc. -- Chay: sqlcmd -S -d -b -i merge-duplicate-projects.sql (ASCII -> khong can -f 65001) -- ============================================================================ -- Filtered-unique index tren PeWorkItemBudgets => BAT BUOC QUOTED_IDENTIFIER ON + -- ANSI_NULLS ON khi UPDATE. Set o batch rieng (GO) de ap cho connection, tranh -- phu thuoc caller nho truyen -I. SET QUOTED_IDENTIFIER ON; SET ANSI_NULLS ON; GO SET NOCOUNT ON; SET XACT_ABORT ON; DECLARE @pairs TABLE (spaceCode nvarchar(50), keepCode nvarchar(50)); INSERT INTO @pairs (spaceCode, keepCode) VALUES (N'FLOCK 01', N'FLOCK01'), (N'FLOCK 03', N'FLOCK03'), (N'CAL 01', N'CAL01'); DECLARE @spaceCode nvarchar(50), @keepCode nvarchar(50); DECLARE @spaceId uniqueidentifier, @keepId uniqueidentifier; DECLARE @spaceCnt int, @keepCnt int, @ct int, @pe int, @bgMove int, @bgCollide int, @liveRef int; DECLARE cur CURSOR LOCAL FAST_FORWARD FOR SELECT spaceCode, keepCode FROM @pairs; OPEN cur; FETCH NEXT FROM cur INTO @spaceCode, @keepCode; WHILE @@FETCH_STATUS = 0 BEGIN SELECT @spaceCnt = COUNT(*), @spaceId = MIN(Id) FROM Projects WHERE Code = @spaceCode AND IsDeleted = 0; SELECT @keepCnt = COUNT(*), @keepId = MIN(Id) FROM Projects WHERE Code = @keepCode AND IsDeleted = 0; IF @spaceCnt = 0 PRINT '[SKIP] ' + @spaceCode + ' -> ' + @keepCode + ' : khong co ban co-dau-cach song (da gop / khong ton tai).'; ELSE IF @spaceCnt > 1 PRINT '[ABORT] ' + @spaceCode + ' : >1 ban song cung code — can nguoi xem tay.'; ELSE IF @keepCnt = 0 PRINT '[ABORT] ' + @spaceCode + ' -> ' + @keepCode + ' : KHONG co ban giu (no-space) song — dung gop.'; ELSE IF @keepCnt > 1 PRINT '[ABORT] ' + @keepCode + ' : >1 ban giu song (reseed cycle?) — reconcile 1 GUID truoc.'; ELSE BEGIN BEGIN TRAN; UPDATE Contracts SET ProjectId = @keepId WHERE ProjectId = @spaceId; SET @ct = @@ROWCOUNT; UPDATE PurchaseEvaluations SET ProjectId = @keepId WHERE ProjectId = @spaceId; SET @pe = @@ROWCOUNT; -- Budget: repoint neu survivor chua co live-budget cung WorkItem UPDATE b SET b.ProjectId = @keepId FROM PeWorkItemBudgets b WHERE b.ProjectId = @spaceId AND b.IsDeleted = 0 AND NOT EXISTS (SELECT 1 FROM PeWorkItemBudgets s WHERE s.ProjectId = @keepId AND s.WorkItemId = b.WorkItemId AND s.IsDeleted = 0); SET @bgMove = @@ROWCOUNT; -- Budget: collision (survivor da co) -> soft-delete ban space UPDATE b SET b.IsDeleted = 1, b.DeletedAt = SYSUTCDATETIME() FROM PeWorkItemBudgets b WHERE b.ProjectId = @spaceId AND b.IsDeleted = 0 AND EXISTS (SELECT 1 FROM PeWorkItemBudgets s WHERE s.ProjectId = @keepId AND s.WorkItemId = b.WorkItemId AND s.IsDeleted = 0); SET @bgCollide = @@ROWCOUNT; -- Budget da soft-delete: repoint de gon audit (khong dung unique filter IsDeleted=0) UPDATE PeWorkItemBudgets SET ProjectId = @keepId WHERE ProjectId = @spaceId AND IsDeleted = 1; SET @liveRef = (SELECT COUNT(*) FROM Contracts WHERE ProjectId = @spaceId) + (SELECT COUNT(*) FROM PurchaseEvaluations WHERE ProjectId = @spaceId) + (SELECT COUNT(*) FROM PeWorkItemBudgets WHERE ProjectId = @spaceId AND IsDeleted = 0); IF @liveRef <> 0 BEGIN ROLLBACK TRAN; PRINT '[ABORT] ' + @spaceCode + ' : con ' + CAST(@liveRef AS varchar(9)) + ' live-ref sau repoint — ROLLBACK, KHONG an mem.'; END ELSE BEGIN UPDATE Projects SET IsDeleted = 1, DeletedAt = SYSUTCDATETIME() WHERE Id = @spaceId; COMMIT TRAN; PRINT '[OK] ' + @spaceCode + ' -> ' + @keepCode + ' : Ct=' + CAST(@ct AS varchar(9)) + ' PE=' + CAST(@pe AS varchar(9)) + ' BgMoved=' + CAST(@bgMove AS varchar(9)) + ' BgCollideSoftDel=' + CAST(@bgCollide AS varchar(9)) + ' ; da an mem ban space.'; END END FETCH NEXT FROM cur INTO @spaceCode, @keepCode; END CLOSE cur; DEALLOCATE cur;