如何仅用字符串函数在SQL Server 2008 R2中解析分隔字符串为列?
优化SQL Server 2008 R2中Char(7)分隔字符串的列解析方案
嘿,针对你在SQL Server 2008 R2里解析char(7)分隔字符串的需求——要拆成最多7列,超过7个组件的话把剩下的全塞最后一列,还不能用UDF、XML或者STRING_SPLIT,我给你优化了一个比原来暴力写法清爽太多的方案,一起来看看:
核心思路
原来的代码重复嵌套调用CHARINDEX,看着头都大。我们换个思路:用CROSS APPLY分步计算每个分隔符的位置,每个位置只算一次,这样代码结构清晰,还不影响性能:
- 先算出第1个
char(7)的位置,再基于它算第2个,一直到第6个(因为最多7列,只需要前6个分隔符的位置就能拆分) - 最后根据这些预计算的位置提取每一列,第7列直接取最后一个分隔符之后的所有内容(包含多余的分隔符和组件)
优化后的代码
CREATE TABLE #x (a INT, delimited_value VARCHAR(8000)) INSERT INTO #x VALUES (1, 'abc'), (2, 'defgh' + CHAR(7) + 'ij' + CHAR(7) + 'klmnop'), (3, ''), (4, 'qr' + CHAR(7) + 's' + CHAR(7) + 't' + CHAR(7) + 'u' + CHAR(7) + 'v' + CHAR(7) + 'w' + CHAR(7) + 'xyz'), (5, '012' + CHAR(7) + CHAR(7) + '3' + CHAR(7)), (6, CHAR(7) + CHAR(7) + '4567' + CHAR(7) + CHAR(7) + '89') SELECT a, -- 第1列:从字符串开头到第1个分隔符前 SUBSTRING(dv.val, 1, ISNULL(NULLIF(pos1, 0), 8000) - 1) AS component1, -- 第2列:第1个分隔符后到第2个分隔符前 CASE WHEN pos1 > 0 THEN SUBSTRING(dv.val, pos1 + 1, ISNULL(NULLIF(pos2, 0), 8000) - pos1 - 1) ELSE '' END AS component2, -- 第3列:第2个分隔符后到第3个分隔符前 CASE WHEN pos2 > 0 THEN SUBSTRING(dv.val, pos2 + 1, ISNULL(NULLIF(pos3, 0), 8000) - pos2 - 1) ELSE '' END AS component3, -- 第4列:第3个分隔符后到第4个分隔符前 CASE WHEN pos3 > 0 THEN SUBSTRING(dv.val, pos3 + 1, ISNULL(NULLIF(pos4, 0), 8000) - pos3 - 1) ELSE '' END AS component4, -- 第5列:第4个分隔符后到第5个分隔符前 CASE WHEN pos4 > 0 THEN SUBSTRING(dv.val, pos4 + 1, ISNULL(NULLIF(pos5, 0), 8000) - pos4 - 1) ELSE '' END AS component5, -- 第6列:第5个分隔符后到第6个分隔符前 CASE WHEN pos5 > 0 THEN SUBSTRING(dv.val, pos5 + 1, ISNULL(NULLIF(pos6, 0), 8000) - pos5 - 1) ELSE '' END AS component6, -- 第7列:第6个分隔符后的所有内容(包含多余的分隔符和组件) CASE WHEN pos6 > 0 THEN SUBSTRING(dv.val, pos6 + 1, 8000) WHEN pos5 > 0 THEN SUBSTRING(dv.val, pos5 + 1, 8000) WHEN pos4 > 0 THEN SUBSTRING(dv.val, pos4 + 1, 8000) WHEN pos3 > 0 THEN SUBSTRING(dv.val, pos3 + 1, 8000) WHEN pos2 > 0 THEN SUBSTRING(dv.val, pos2 + 1, 8000) WHEN pos1 > 0 THEN SUBSTRING(dv.val, pos1 + 1, 8000) ELSE dv.val END AS component7 FROM #x -- 给原字符串起个别名,方便后续引用 CROSS APPLY (SELECT delimited_value AS val) dv -- 分步计算每个分隔符的位置 CROSS APPLY (SELECT CHARINDEX(CHAR(7), dv.val) AS pos1) p1 CROSS APPLY (SELECT CHARINDEX(CHAR(7), dv.val, pos1 + 1) AS pos2) p2 CROSS APPLY (SELECT CHARINDEX(CHAR(7), dv.val, pos2 + 1) AS pos3) p3 CROSS APPLY (SELECT CHARINDEX(CHAR(7), dv.val, pos3 + 1) AS pos4) p4 CROSS APPLY (SELECT CHARINDEX(CHAR(7), dv.val, pos4 + 1) AS pos5) p5 CROSS APPLY (SELECT CHARINDEX(CHAR(7), dv.val, pos5 + 1) AS pos6) p6 DROP TABLE #x
为什么这个方案更好?
- 可读性拉满:再也没有嵌套到怀疑人生的
CHARINDEX调用,每一步的分隔符位置都单独计算,逻辑一目了然。 - 性能不打折:和原方案一样只用了内置字符串函数,没有额外开销,甚至因为减少了重复计算,性能可能还略胜一筹。
- 逻辑更严谨:第7列的处理直接覆盖了所有情况——不管有多少个分隔符,最后一列都会包含剩余的所有内容,完全符合你的需求。
- 灵活调整:如果需要把空组件(比如连续两个
char(7)之间的内容)显示为NULL而不是空字符串,只要把前6列ELSE ''去掉就行。
内容的提问来源于stack exchange,提问作者bvy
相关产品推荐
相关产品推荐

