You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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;

代码说明

  1. CleanAddresses CTE:负责地址预处理,把换行符(CHAR(10))、回车符(CHAR(13))、分号(;)替换成空格,再通过循环把多个连续空格合并成单个,避免拆分时出现无效的空格拆分点。
  2. AddressSegments CTE:递归拆分地址。每一次递归都会计算当前段的最大有效长度(不超过35且不拆分单词),然后截取分段,同时保留剩余未拆分的地址内容,直到内容为空或者达到5段的上限。
  3. 最终行转列:通过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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.23 10:10:30