SQL Server 2017中按空格和35字符长度拆分地址字符串为多列(避免拆分单词)
SQL Server 2017中按空格和35字符长度拆分地址字符串为多列(避免拆分单词)
嘿,我完全懂你现在的难处——把那种自由输入、毫无规律的长地址拆成固定35字符的列,还不能把单词拦腰截断,之前写的那些嵌套CASE表达式看着都头大,维护起来更是噩梦。不如试试用递归CTE的方式来实现,逻辑清晰多了,还能覆盖各种边界情况。
解决方案思路
核心思路是先把地址里的特殊换行、回车、分号统一换成空格,再合并连续空格避免干扰,然后用递归的方式逐段拆分:每一次都找到当前待拆分内容中不超过35字符的最后一个空格作为拆分点,直到内容拆完或者达到5列的上限。
完整实现代码
WITH CleanAddresses AS ( -- 第一步:清理原始地址,替换特殊符号并合并连续空格 SELECT ROW_ID, CleanAddress = LTRIM(RTRIM( REPLACE(REPLACE(REPLACE( OE.ADDR, CHAR(10), ' '), CHAR(13), ' '), ';', ',') )) FROM IDTB_ORG_EXT OE -- 循环替换多个连续空格为单个空格 UNION ALL SELECT ROW_ID, CleanAddress = REPLACE(CleanAddress, ' ', ' ') FROM CleanAddresses WHERE PATINDEX('% %', CleanAddress) > 0 ), AddressSegments AS ( -- 递归拆分地址段,最多生成5个分段 SELECT ROW_ID, SegmentNumber = 1, -- 计算当前分段的结束位置:如果长度<=35则直接取全部,否则找35字符内最后一个空格 SegmentEnd = CASE WHEN LEN(CleanAddress) <= 35 THEN LEN(CleanAddress) ELSE 35 - CHARINDEX(' ', REVERSE(LEFT(CleanAddress, 35))) END, Segment = LEFT(CleanAddress, CASE WHEN LEN(CleanAddress) <= 35 THEN LEN(CleanAddress) ELSE 35 - CHARINDEX(' ', REVERSE(LEFT(CleanAddress, 35))) END), RemainingAddress = LTRIM(RIGHT(CleanAddress, LEN(CleanAddress) - CASE WHEN LEN(CleanAddress) <= 35 THEN LEN(CleanAddress) ELSE 35 - CHARINDEX(' ', REVERSE(LEFT(CleanAddress, 35))) END)) FROM CleanAddresses WHERE CleanAddress <> '' UNION ALL SELECT ROW_ID, SegmentNumber = SegmentNumber + 1, SegmentEnd = CASE WHEN LEN(RemainingAddress) <= 35 THEN LEN(RemainingAddress) ELSE 35 - CHARINDEX(' ', REVERSE(LEFT(RemainingAddress, 35))) END, Segment = LEFT(RemainingAddress, CASE WHEN LEN(RemainingAddress) <= 35 THEN LEN(RemainingAddress) ELSE 35 - CHARINDEX(' ', REVERSE(LEFT(RemainingAddress, 35))) END), RemainingAddress = LTRIM(RIGHT(RemainingAddress, LEN(RemainingAddress) - CASE WHEN LEN(RemainingAddress) <= 35 THEN LEN(RemainingAddress) ELSE 35 - CHARINDEX(' ', REVERSE(LEFT(RemainingAddress, 35))) END)) FROM AddressSegments WHERE RemainingAddress <> '' AND SegmentNumber < 5 -- 限制最多生成5个地址列 ) -- 把递归生成的行转成对应的Address_1到Address_5列 SELECT ROW_ID, Address_1 = ISNULL(MAX(CASE WHEN SegmentNumber = 1 THEN Segment ELSE '' END), ''), Address_2 = ISNULL(MAX(CASE WHEN SegmentNumber = 2 THEN Segment ELSE '' END), ''), Address_3 = ISNULL(MAX(CASE WHEN SegmentNumber = 3 THEN Segment ELSE '' END), ''), Address_4 = ISNULL(MAX(CASE WHEN SegmentNumber = 4 THEN Segment ELSE '' END), ''), Address_5 = ISNULL(MAX(CASE WHEN SegmentNumber = 5 THEN Segment ELSE '' END), '') FROM AddressSegments GROUP BY ROW_ID ORDER BY ROW_ID;
代码说明
- CleanAddresses CTE:负责地址预处理,把换行符(
CHAR(10))、回车符(CHAR(13))、分号(;)替换成空格,再通过循环把多个连续空格合并成单个,避免拆分时出现无效的空格拆分点。 - AddressSegments CTE:递归拆分地址。每一次递归都会计算当前段的最大有效长度(不超过35且不拆分单词),然后截取分段,同时保留剩余未拆分的地址内容,直到内容为空或者达到5段的上限。
- 最终行转列:通过
CASE和MAX函数把递归生成的多行分段数据转成目标系统需要的Address_1到Address_5列。
测试你的示例地址
用你提供的示例地址'Cecilia Chapman 711-2880 Nulla St. Mankato Mississippi 96522 (257) 563-7401'测试,会得到完全符合预期的结果:
Address_1 = 'Cecilia Chapman 711-2880 Nulla'Address_2 = 'St. Mankato Mississippi 96522'Address_3 = '(257) 563-7401'Address_4和Address_5为空
这个方案比你之前写的嵌套CASE表达式更易读、易维护,还能处理各种复杂的地址格式~
备注:内容来源于stack exchange,提问作者Sven
相关产品推荐
相关产品推荐

