T-SQL 內建 STRING_SPLIT(SQL 2016+)不保證順序、不支援多字元分隔符、無法保留空值。自製 SplitString 解決三大痛點。
CREATE FUNCTION dbo.SplitString
(
@Input NVARCHAR(MAX),
@Separator NVARCHAR(MAX) = N',',
@RemoveEmptyEntries BIT = 1
)
RETURNS @Result TABLE (Id INT IDENTITY(1,1) PRIMARY KEY, Value NVARCHAR(MAX))
AS
BEGIN
DECLARE @Idx INT, @Token NVARCHAR(MAX);
SET @Idx = CHARINDEX(@Separator, @Input);
WHILE @Idx > 0
BEGIN
SET @Token = LTRIM(RTRIM(SUBSTRING(@Input, 1, @Idx - 1)));
IF @RemoveEmptyEntries = 0 OR @Token <> N''
INSERT INTO @Result(Value) VALUES(@Token);
SET @Input = SUBSTRING(@Input, @Idx + DATALENGTH(@Separator)/2, LEN(@Input));
SET @Idx = CHARINDEX(@Separator, @Input);
END
-- 最後一段
SET @Token = LTRIM(RTRIM(@Input));
IF @RemoveEmptyEntries = 0 OR @Token <> N''
INSERT INTO @Result(Value) VALUES(@Token);
RETURN;
END;
DECLARE @s1 VARCHAR(MAX) = '1,2,3',
@s2 VARCHAR(MAX) = '1###2###3',
@s3 VARCHAR(MAX) = '1###2###3###';
SELECT * FROM dbo.SplitString(@s1, ',', 1); -- 1 / 2 / 3
SELECT * FROM dbo.SplitString(@s2, '###', 1); -- 1 / 2 / 3
SELECT * FROM dbo.SplitString(@s3, '###', 0); -- 1 / 2 / 3 / (空行保留)
| 方案 | 優點 | 缺點 | 適用場景 |
|---|---|---|---|
| 本函數 (WHILE 迴圈) | 支援多字元、保序、可選保留空值 | 極大字串(>10 萬字元)較慢 | 一般業務分割、ETL 前置 |
XML 法 CAST(' | 集合式、無迴圈 | 特殊字元需轉義、記憶體佔用大 | 中小字串、臨時查詢 |
JSON 法 OPENJSON('["'+REPLACE(...)+'"]') | SQL 2016+ 原生、效能佳 | 需合法 JSON、分隔符固定為逗號 | 現代版本、標準化分割 |
STRING_SPLIT + ORDINAL (SQL 2022) | 原生、高效能、保序 | 不支援多字元、不保留空值 | 簡單逗號分割、大數據量 |
若追求極致效能,可改寫為 RETURNS TABLE ... AS RETURN SELECT ... 形式,配合 Tally Table 或 STRING_SPLIT 實現無迴圈集合運算,並能被優化器內聯展開。
|、.)時,本函數以字面匹配,無需轉義。一個靈活的分割函數,能讓許多「臨時拆分」需求變得優雅可控。