兩種方案均依賴自定義表值函數 split(@str, @delimiter) 返回單列表(每行一個片段),請確保數據庫已部署該函數(網上開源實現眾多,此處不贅述)。
DECLARE @Data NVARCHAR(MAX) = '1,tanw;2,keenboy'; -- 格式:Id,Name;Id,Name...
DECLARE @dataItem NVARCHAR(100);
DECLARE data_cursor CURSOR LOCAL FAST_FORWARD FOR
SELECT * FROM split(@Data, ';'); -- 外層:按分號切分記錄
OPEN data_cursor;
FETCH NEXT FROM data_cursor INTO @dataItem;
WHILE @@FETCH_STATUS = 0
BEGIN
DECLARE @Id INT, @Name NVARCHAR(50);
DECLARE dataItem_cursor CURSOR LOCAL FAST_FORWARD FOR
SELECT * FROM split(@dataItem, ','); -- 內層:按逗號切分字段
OPEN dataItem_cursor;
FETCH NEXT FROM dataItem_cursor INTO @Id;
FETCH NEXT FROM dataItem_cursor INTO @Name;
CLOSE dataItem_cursor; DEALLOCATE dataItem_cursor;
-- ===== 在這裡寫業務邏輯:MERGE / IF EXISTS UPDATE ELSE INSERT =====
-- MERGE INTO TargetTable USING (VALUES(@Id,@Name)) AS S(Id,Name) ...
-- 或傳統 IF EXISTS ... UPDATE ... ELSE INSERT ...
FETCH NEXT FROM data_cursor INTO @dataItem;
END
CLOSE data_cursor; DEALLOCATE data_cursor;
DECLARE @Data NVARCHAR(MAX) = 'tanw,keenboy'; -- 僅演示 Name 列表
DECLARE @Temp TABLE (Id INT IDENTITY(1,1), Name NVARCHAR(50));
INSERT @Temp SELECT * FROM split(@Data, ','); -- 一次性拆分入表變量
DECLARE @Id INT, @Name NVARCHAR(50), @Results NVARCHAR(MAX) = '';
WHILE EXISTS (SELECT 1 FROM @Temp)
BEGIN
SELECT TOP 1 @Id = Id, @Name = Name FROM @Temp;
DELETE @Temp WHERE Id = @Id;
SET @Results = @Results + @Name + ',';
-- ===== 在這裡寫業務邏輯 =====
END
SELECT @Results AS ProcessedNames;
| 維度 | 游標 | 表變量+WHILE |
|---|---|---|
| 內存佔用 | 低(流式處理) | 中(需載入全量到表變量) |
| 並發/鎖 | 較長持有鎖(游標默認鎖行) | 短鎖(單條 DELETE 快速釋放) |
| 代碼複雜度 | 高(雙層嵌套、資源釋放易漏) | 低(線性邏輯、自動清理) |
| 適用場景 | 極大數據流、需逐行提交事務、外部 API 調用 | 中小批次、純數據庫邏輯、易維護 |
若僅是「將分割後的數據批量 INSERT 同一張表」,請直接用 集合操作:
INSERT INTO TargetTable (Name)
SELECT * FROM split(@Data, ',');
-- 或配合 MERGE 實現 UPSERT
MERGE TargetTable AS T
USING (SELECT * FROM split(@Data, ',')) AS S(Name)
ON T.Name = S.Name
WHEN NOT MATCHED THEN INSERT (Name) VALUES (S.Name);
游標是「手術刀」,精準但沉重;表變量+WHILE 是「瑞士軍刀」,輕便實用。現代 SQL Server (2016+) 優先考慮 STRING_SPLIT / OPENJSON 內置分割 + 集合化 MERGE,告別循環才是正道。