業務需求:MySQL 核心表 accounts 變更(增/刪/改)實時同步至 SQL Server 同名表。方案:MySQL 觸發器寫入備份表 + SQL Server Agent 定時拉取備份表增量 + 存儲過程合并寫入目標表。
MySQL accounts 表
│ AFTER INSERT/UPDATE/DELETE Trigger
▼
MySQL accountsbackup 表(含 BehaviorType: I/U/D, ExecutingState, ModificationTime)
│ SQL Server Linked Server (OpenQuery) 定時輪詢
▼
SQL Server 存儲過程 trigger_account
│ 分三步:INSERT(I) / DELETE(D) / UPDATE(U)
▼
SQL Server account 表
CREATE TABLE `accountsbackup` ( `id` char(36) NOT NULL, ... -- 所有業務字段同步 `BehaviorType` varchar(45) DEFAULT NULL, -- 'I' Insert, 'U' Update, 'D' Delete `ExecutingState` varchar(45) DEFAULT NULL, -- NULL=待同步, 'D'=已同步 `ModificationTime` varchar(45) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
注意:id 不設唯一索引(同一記錄多次變更產生多行)。
-- 插入觸發器 DELIMITER || CREATE TRIGGER t_afterinsert_on_accounts AFTER INSERT ON accounts FOR EACH ROW BEGIN INSERT INTO accountsbackup (id, name, ..., BehaviorType) VALUES (NEW.id, NEW.name, ..., 'I'); END|| -- 更新觸發器 CREATE TRIGGER t_afterupdate_on_accounts AFTER UPDATE ON accounts FOR EACH ROW BEGIN INSERT INTO accountsbackup (id, name, ..., BehaviorType) VALUES (NEW.id, NEW.name, ..., 'U'); END|| -- 刪除觸發器 CREATE TRIGGER t_afterdelete_on_accounts AFTER DELETE ON accounts FOR EACH ROW BEGIN INSERT INTO accountsbackup (id, name, ..., BehaviorType) VALUES (OLD.id, OLD.name, ..., 'D'); END|| DELIMITER ;
CREATE TABLE [dbo].[account] ( [id] [char](36) NOT NULL, [name] [varchar](150) NULL, ... -- 字段類型映射:char→char, varchar→varchar, text→text, tinyint→smallint PRIMARY KEY ([id]) );
trigger_account 邏輯ALTER PROCEDURE [dbo].[trigger_account] AS
BEGIN
-- 1. 處理插入 (BehaviorType='I' 且 ExecutingState IS NULL)
INSERT INTO account (id, name, ...)
SELECT rtrim(id), name, ... -- 關鍵:RTRIM 去除 CHAR 尾部空格
FROM OPENQUERY(MySQL, 'SELECT ... FROM accountsbackup WHERE ExecutingState IS NULL AND BehaviorType=''I''');
UPDATE OPENQUERY(MySQL, 'SELECT * FROM accountsbackup WHERE ExecutingState IS NULL AND BehaviorType=''I''')
SET ExecutingState='D', ModificationTime=GETDATE();
-- 2. 處理刪除 (BehaviorType='D')
SELECT rtrim(id) INTO #temp FROM OPENQUERY(MySQL, 'SELECT id FROM accountsbackup WHERE ExecutingState IS NULL AND BehaviorType=''D''');
IF (SELECT COUNT(*) FROM #temp) > 0 BEGIN
DELETE FROM account WHERE id IN (SELECT rtrim(id) FROM #temp);
UPDATE OPENQUERY(MySQL, 'SELECT * FROM accountsbackup WHERE ExecutingState IS NULL AND BehaviorType=''D''')
SET ExecutingState='D', ModificationTime=GETDATE();
END
-- 3. 處理更新 (BehaviorType='U') - 最複雜:UPSERT 邏輯
SELECT rtrim(id) id, name, ... INTO #temp1 FROM OPENQUERY(MySQL, 'SELECT ... FROM accountsbackup WHERE ExecutingState IS NULL AND BehaviorType=''U''');
IF (SELECT COUNT(*) FROM #temp1) > 0 BEGIN
-- 存在則更新
UPDATE account SET ... FROM account INNER JOIN #temp1 t ON account.id = t.id;
-- 不存在則插入(極少見,但需處理)
INSERT INTO account (...) SELECT ... FROM #temp1 t WHERE t.id NOT IN (SELECT id FROM account);
UPDATE OPENQUERY(MySQL, 'SELECT * FROM accountsbackup WHERE ExecutingState IS NULL AND BehaviorType=''U''')
SET ExecutingState='D', ModificationTime=GETDATE();
END
END
CHAR(36) 存 UUID 不補空格,SQL Server CHAR(36) 會右填空格。**必須 RTRIM**,否則 JOIN/WHERE 失敗。SELECT、UPDATE(更新 ExecutingState)權限。ExecutingState 狀態機保證重複執行不重複同步。觸發器+輪詢方案適合表少、延遲容忍度高、無中間件條件的輕量級場景;核心業務建議上 CDC 平台。