撰寫跨伺服器、跨版本(SQL 6.5/2000)、大量資料、複雜業務邏輯的預存程序,全後台處理。過程中踩坑眾多,經同事協助調優後程序可跑通,但效能仍待改善。整理六大核心經驗原則。
-- 一次建立,處處引用
EXEC sp_addlinkedserver
@server = 'LINK_PROD',
@srvproduct = 'SQL Server';
EXEC sp_addlinkedsrvlogin
@rmtsrvname = 'LINK_PROD',
@useself = 'FALSE',
@locallogin = NULL,
@rmtuser = 'remote_user',
@rmtpassword = 'password';
OPENQUERY 或遠端預存程序,在遠端完成篩選、彙總,只回傳結果集。SELECT col1, col2 而非 SELECT *,減少網路頻寬與序列化開銷。
-- 錯誤做法:直接跨伺服器 JOIN
SELECT * FROM LocalTable L
JOIN LinkedServer.DB.dbo.RemoteTable R ON L.id = R.id;
-- 正確做法:先抓遠端資料到本地暫存表
SELECT id, col1, col2 INTO #RemoteCache
FROM OPENQUERY(LinkedServer, 'SELECT id, col1, col2 FROM RemoteTable WHERE ...');
-- 再本地 JOIN
SELECT * FROM LocalTable L
JOIN #RemoteCache R ON L.id = R.id;
WHERE 在 OPENQUERY 內)。
-- JOIN/比較欄位後加 COLLATE 強制統一
SELECT * FROM LocalTable L
JOIN #RemoteCache R ON L.code COLLATE Chinese_PRC_CI_AS = R.code COLLATE Chinese_PRC_CI_AS;
COLLATE Chinese_PRC_CI_AS(或專案統一 Collation)消除歧義。游標危害:逐列處理(RBAR),鎖定時間長、鎖升級風險高、計畫快取無法重用、平行度受限。
-- 以單一 UPDATE/INSERT/DELETE 替代游標迴圈
UPDATE TargetTable
SET status = 'Processed'
WHERE id IN (SELECT id FROM SourceTable WHERE condition);
-- 建立帶身分識別碼的暫存表
SELECT IDENTITY(INT,1,1) AS Seq, id, col1, col2
INTO #Batch
FROM SourceTable WHERE condition;
DECLARE @MaxSeq INT = (SELECT MAX(Seq) FROM #Batch);
DECLARE @CurSeq INT = 1;
WHILE @CurSeq <= @MaxSeq
BEGIN
SELECT @id = id, @col1 = col1, @col2 = col2 FROM #Batch WHERE Seq = @CurSeq;
-- 單筆處理邏輯
SET @CurSeq = @CurSeq + 1;
END
SELECT 一列,避免游標持有鎖。跨伺服器大量資料處理的核心哲學:把運算搬到資料旁邊(下推),把資料搬到運算旁邊(暫存表),用集合思維替代迴圈思維。這六大原則不僅適用於 SQL Server 2000,對現代雲端資料庫、分散式查詢引擎同樣有效。