更新时间:2025-12-01 GMT+08:00
分享

截取字符串

在数据仓库多维分析场景中,字符串截取是支撑业务分析的关键技术能力。

典型场景包括:

  • 提取核心信息:对复合订单号进行智能分割,例如将"EC2023-BJ-0129A"拆解出核心序列号"0129A",在18位身份证编码中使用substring()函数同时捕获行政区划代码、出生年月信息等。
  • 脱敏数据:用户隐私数据处理,例如通过right()函数精准提取手机号末4位实现数据脱敏。
  • 精准提取信息:支持按正则表达式进行匹配提取,从而精准提取有效信息,例如通过regexp_matches函数匹配出多个以b字母开头的字符串。
  • 清除冗余数据:例如清除某个订单号信息前后的多余空格或空字符。

DWS提供多种截取字符串的函数供用户选择。本章节中函数所使用的示例大部分以英文字符串示例为主,如果示例中存在中文字符串,则一般指UTF-8字符编码下的场景示例,字符编码在创建数据库时指定,参见CREATE DATABASE

表1 截取字符串常用函数

分类

功能

对应函数

简单示例

使用区别

去除两端字符

去除字符串两端指定的字符或空格。

btrim(string text [, characters text])

1
2
3
4
5
SELECT btrim('   sting  ');   
btrim
-------
 sting
(1 row)

-

提取子串,单位字节

提取子串,从第几个字节开始提取,一共提取几个字节。

substrb(string,from,count)

1
2
3
4
5
SELECT substrb('ABCDEF',2,2);
 substrb 
---------
 BC
(1 row)

四个函数的功能很相似,区别是提取剩余完整部分,还是只提取中间几个字节,入参是text类型,还是所有类字符串。

提取子串,从第几个字节开始提取,一共提取几个字节。功能同上,区别是强调入参类型是text。

substrb(text,int,int)

-

提取子串,从第几个字节开始提取,提取剩余完整部分,当from是负数时,可以表示截取后边n个字节。

substrb(string,from)

1
2
3
4
5
SELECT substrb('ABCDEF',-2);
 substrb 
---------
 EF
(1 row)

提取子串,从第几个字节开始提取,提取剩余完整部分。功能同上,区别是强调入参类型是text。

substrb(text,int)

1
2
3
4
5
SELECT substrb('string',2);
 substrb
---------
 tring
(1 row)

提取子串,单位字符

提取子串,从第几个字符开始提取,一共提取几个字符。

substr(string,from,count)

1
2
3
4
5
SELECT substr('database',0,4);
 substr
--------
 data
(1 row)

是提取剩余完整部分,还是只提取中间几个字符,substr(string,from,count)substring(string [from int] [for int])高度相似,后者是标准SQL,前者更简洁。

提取子串,从第几个字符开始提取,提取剩余完整部分。

substr(string,from)

1
2
3
4
5
SELECT substr('database',5);
 substr
--------
 base
(1 row)

提取子串,从第几个字符开始提取,一共提取几个字符。与substr(string,from,count)功能一致。

substring(string [from int] [for int])

-

提取前几位字符。

left(str text, n int)

-

-

提取后几位字符。

right(str text, n int)

-

-

按正则表达式提取子串

提取子串,匹配某种表达式,符合了才截取成功。

substring(string from pattern)

单击左侧链接跳转查看。

regexp_substr函数与substring函数类似,substring函数是SQL标准,regexp_substr属于扩展函数,正则能力更强。

regexp_matches返回类型为文本数组,适合提取分组信息的场景。

提取子串,匹配某种表达式,符合了才截取成功,支持自定义转义符。

substring(string from pattern for escape)

功能同上类似,但是正则能力强。

regexp_substr(text,text)

功能同上类似,但是正则能力强。

regexp_substr(source_char, pattern)

提取子串,但是返回类型是文本数组,尤其适合需要多条件匹配或提取分组信息的场景。

regexp_matches(string text, pattern text [, flags text])

btrim(string text [, characters text])

描述:去除字符串(string)两端指定的字符。仅处理两端字符,中间字符即使匹配也不会被移除。

返回值类型:text

参数说明:

  • string,必选,目标字符串。
  • characters,可选,要在目标字符串两端移除的字符。characters参数缺省时,默认去除所有的空白字符。

示例:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
SELECT btrim('sring' , 'ing');
 btrim
-------
 sr
(1 row)

SELECT btrim('   sting  ');   --不指定characters,默认去除多余空格
 btrim
-------
 sting
(1 row)

SELECT btrim('xxdataxxbasexx','x');    --注意只去除两端的x,中间有x也不会去除。
 btrim
-------
 dataxxbase
(1 row)

substrb(string,from,count)

描述:从字符串中截取指定长度的子字符串。from表示截取的起始位置,count表示截取的子字符串长度。

该函数和substr(string,from,count)函数功能一致,但是计算单位为字节。

  • from为0时,按1处理。
  • from为正数时,截取从from开始的count个字节。
  • from为负数时,截取从倒数第n个开始的count个字节,n为from的绝对值。
  • count小于1时,返回null。

返回值类型:bytea

示例:

1
2
3
4
5
SELECT substrb('ABCDEF',2,2);
 substrb 
---------
 BC
(1 row)

substrb(text,int,int)

描述:从字符串中提取子字符串,第一个int表示从第几个字节位置开始提取,第二个int表示提取几个字节。

返回值类型:text

示例:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
SELECT substrb('string',2,3);     ---从第2个字节“t”开始,提取3个字节。
 substrb
---------
 tri
(1 row)

SELECT substrb('学习数据库知识',7,9);     --UTF8编码下,一个中文占3个字节,从第7个字节位置开始,即跳过2个中文字,从“数”开始,截取9个字节(3个中文)
 substrb
---------
 数据库
(1 row)

SELECT substrb('学习数据库知识',7,9);     --GBK编码下,一个中文占2个字节,从第7个字节位置开始,即跳过3个中文字,从“据”开始,截取9个字节(4个中文)
 substrb
---------
 据库知识
(1 row)

substrb(string,from)

描述:从字符串中截取指定的子字符串,from表示截取的起始位置。该函数和substr(string,from)函数功能一致,但是计算单位为字节。

  • from为0时,按1处理。
  • from为正数时,截取从from到末尾的所有字节。
  • from为负数时,截取字符串的后n个字节,n为from的绝对值。

返回值类型:bytea

示例:

1
2
3
4
5
SELECT substrb('ABCDEF',-2);
 substrb 
---------
 EF
(1 row)

substrb(text,int)

描述:从字符串中提取子字符串,int表示从第几个字节开始提取,提取后面剩余的完整部分。

返回值类型:text

示例:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
SELECT substrb('string',2);
 substrb
---------
 tring
(1 row)

SELECT substrb('学习数据库知识',7);     --UTF8编码下,一个中文占3个字节,从第7个字节位置开始,即跳过2个中文字,从“数”开始截取后面完整部分
 substrb
---------
 数据库知识
(1 row)

substr(string,from,count)

描述:从字符串中截取指定长度的子字符串。

from表示截取的起始位置,count表示截取的子字符串长度。

  • from为0时,按1处理。
  • from为正数时,截取从from开始的count个字符。
  • from为负数时,截取从倒数第n个开始的count个字符,n为from的绝对值。
  • count小于1时,返回null。

返回值类型:varchar

示例:

from为0时,截取“database”从第1个字符开始的4个字符。

1
2
3
4
5
SELECT substr('database',0,4);
 substr
--------
 data
(1 row)

from为正数时,截取“database”从第5个字符开始的4个字符。

1
2
3
4
5
SELECT substr('database',5,4);
 substr
--------
 base
(1 row)

from为负数时,截取“database”从倒数第4个字符开始的3个字符。

1
2
3
4
5
SELECT substr('database',-4,3);
 substr
--------
 bas
(1 row)

substr(string,from)

描述:从字符串中截取指定的子字符串。

from表示截取的起始位置。

  • from为0时,按1处理。
  • from为正数时,截取从from到末尾的所有字符。
  • from为负数时,截取字符串的后n个字符,n为from的绝对值。

返回值类型:varchar

示例:

from为0时,截取“database”从第1个字符至末尾的所有字符。

1
2
3
4
5
SELECT substr('database',0);
  substr
----------
 database
(1 row)

from为正数时,截取“database”从第5个字符至末尾的所有字符。

1
2
3
4
5
SELECT substr('database',5);
 substr
--------
 base
(1 row)

from为负数时,截取“database”从倒数第4个字符开始至末尾的所有字符。

1
2
3
4
5
SELECT substr('database',-4);
 substr
--------
 base
(1 row)

substring(string [from int] [for int])

描述:截取子字符串,from int表示从第几个字符开始截取,for int表示截取几个字符。与substr(string,from,count)功能一致。

返回值类型:text

示例:

从字符串“Thomas”的第2个字符“h”开始连续截取3个字符,即返回“hom”。

1
2
3
4
5
SELECT substring('Thomas' from 2 for 3);
 substring
-----------
 hom
(1 row)

left(str text, n int)

描述:返回字符串的前n个字符。

  • ORA和TD兼容模式下,当n是负数时,返回除最后|n|个字符以外的所有字符。
  • MySQL兼容模式下,当n是负数时,返回空串。

返回值类型:text

示例:

1
2
3
4
5
 SELECT left('database', 4);
 left
------
 data
(1 row)

right(str text, n int)

描述:返回字符串中的后n个字符。

  • ORA和TD兼容模式下,当n是负数时,返回除前|n|个字符以外的所有字符。
  • MySQL兼容模式下,当n是负数时,返回空串。

返回值类型:text

示例:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
SELECT right('abcde', 2);
 right
-------
 de
(1 row)

SELECT right('abcde', -2);
 right 
-------
 cde
(1 row)

substring(string from pattern)

描述:截取匹配POSIX正则表达式(也称为模式)的子字符串,其中string表示原始字符串,pattern表示待匹配的模式。如果该字符串没有匹配上模式则返回空值,否则返回文本中匹配到模式的那一部分。

返回值类型:text

示例:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
SELECT substring('Thomas' from '...$');    ---匹配末尾3个连续字符
 substring
-----------
 mas
(1 row)

SELECT substring('Thomas' from '^...');     ---匹配开头3个连续字符
 substring
-----------
 Tho
(1 row)
 
SELECT substring('Tom' from '....$');      ---匹配末尾4个连续字符,Tom仅3个字符,无法匹配条件,结果为空
 substring
-----------

(1 row)

SELECT substring('order_20251120_12345' from '_([0-9]+)_');      ---获取两个“_”之间的订单号
 substring
-----------
 20251120
(1 row)


SELECT substring('tel:13812345678,status:valid' from 'tel:([0-9]{11})');     ---提取手机号信息
 substring
-----------
 13812345678
(1 row)


SELECT substring('foobar' from 'o(.)b');     --匹配o(.)b规则, o表示匹配第一个字母o,  (.)表示匹配任意一个字符,并用括号捕获它,   b表示匹配到字母b。  'foobar' 中,'oob' 是匹配的子串,括号位置取的是子串的中间字符,所以结果是第二个o。
 substring 
--------
 o
(1 row)

SELECT substring('foobar' from '(o(.)b)');   --嵌套捕获规则,外层第一个捕获组,匹配到oob,内层第二个捕获组,匹配到第3个o,函数结果仅返回第一个捕获组,因此结果为oob
 substring 
--------
 oob
(1 row)

如果POSIX正则表达式模式包含任何圆括号,那么将返回匹配第一对子表达式(对应第一个左圆括号的) 的文本。如果想在表达式里使用圆括号而又不想导致这个例外,那么可以在整个表达式外边加上一对圆括号。

substring(string from pattern for escape)

描述:截取匹配SQL正则表达式的子字符串,其中string表示原始字符串,pattern表示匹配的模式,escape表示转义符,通过for进行指定。如果没有匹配到模式则返回空值。

为了标识在成功的时候应该返回的模式部分,模式必须包含转义字符的两次出现,并且每个转义字符后面要跟上双引号(")。如果匹配这两个标记之间的模式的文本将被返回。

返回值类型:text

示例:

1
2
3
4
5
SELECT substring('Thomas' from '%#"o_a#"_' for '#');    
 substring
-----------
 oma
(1 row)

示例解读

  1. %#"o_a#"_ 为匹配的模式,#为转义符。
  2. %表示匹配任意数量的任意前缀的通配符,该例子匹配到前缀Tho
  3. #"为一个标记,该语法的目的是要匹配两个#"之间包含的文本,即o_a
  4. 最后一个_表示匹配到后缀字符s

综上,整个匹配模式体%#"o_a#"_最后展开来是 % o_a _,含义变为匹配「任意前缀」+ o + 「单个任意字符」 + a + 「单个任意字符」。

字符串Thomas如果需要匹配上这个模式的话,即只截取两个#'之间的文本,即o_a,其中_可以表示通配任何字符,所以结果是oma

拓展示例

已知快递单号FX589220251124,结构为FX + 4位单号 + 8位时间戳,实际系统只需要捕捉FX后面的4位,则可以通过以下写法获取。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
SELECT substring('FX589220251124' from 'FX#"____#"%' for '#');
 substring
-----------
 5892
(1 row)

SELECT substring('FX564320241024' from 'FX#"____#"%' for '#');
 substring
-----------
 5643
(1 row)

regexp_substr(text,text)

描述:正则表达式的抽取子串函数。第一个text表示待处理的原始字符串,第二个text为定义的正则表达式。与substr功能相似,正则表达式出现多个并列的括号时,也全部处理。

返回值类型:text

示例:

1
2
3
4
5
SELECT regexp_substr('str','[ac]');    ---从“str”字符串中,匹配“ac”中的任意一个字符,结果为空
 regexp_substr
---------------

(1 row)

示例:从混杂文本中提取第一个手机号。

1
2
3
4
5
SELECT regexp_substr('我的手机号是13812345678,备用号13987654321', '\d{11}');   -- \d{11}表示找11位连续数字
 regexp_substr
---------------
13812345678
(1 row)

示例:从快递单号中取前面10位订单号,10位后面的其它数字为时间戳,可以忽略。

1
2
3
4
5
SELECT regexp_substr('FX58920235822025112404', '\d{10}');    --\d{10} 表示找10位连续数字,匹配过程,从左到右扫描字符串,取第一个符合“10位连续数字”的子串。
 regexp_substr 
---------------
 5892023582
(1 row)

regexp_substr(source_char, pattern)

描述:正则表达式的抽取子串函数,source_char表示原始字符串,pattern表示定义的正则表达式。

返回值类型:varchar

示例:

1
2
3
4
5
6
--匹配【一个逗号】+【至少1个非逗号字符】+【一个逗号】的连续子串
SELECT regexp_substr('500 Hello World, Redwood Shores, CA', ',[^,]+,') "REGEXPR_SUBSTR";
  REGEXPR_SUBSTR   
-------------------
 , Redwood Shores,
(1 row)

regexp_matches(string text, pattern text [, flags text])

描述:返回string中所有匹配POSIX正则表达式的子字符串,返回结果是文本数组。如果pattern不匹配,该函数不返回行。这个函数在处理复杂文本解析时非常灵活,尤其适合需要多条件匹配或提取分组信息的场景。

  • string为原始字符串。
  • pattern为匹配模式。pattern可以是一个包含多个带圆括号的子表达式,例如(bar)(beque),匹配成功后返回对应的含有多个元素的文本数组,例如{bar,beque};如果pattern不包含带有圆括号的子表达式,则返回一个单一元素的文本数组,例如{barbeque}
  • flags为可选参数,取值为i表示大小写不敏感,g表示替换每一个匹配的子字符串而不仅仅是第一个,即全局匹配。

如果提供了最后一个参数flags,但参数值是空字符串(''),且数据库SQL兼容模式设置为ORA的情况下,会导致返回结果为空集。这是因为ORA兼容模式将''作为NULL处理,避免此类行为的方式有如下几种:

  • 将数据库SQL兼容模式改为TD。
  • 不提供最后一个参数,或最后一个参数不为空字符串。

返回值类型:多行文本数组 setof text[]

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
--匹配规则包含两个捕获分组,第一个(bar),匹配字面bar, 第二个(beque),匹配字面beque,结果为包含2个元素的文本数组
SELECT regexp_matches('foobarbequebaz', '(bar)(beque)');   
 regexp_matches
----------------
 {bar,beque}
(1 row)


--不带括号的匹配规则,结果为只包含1个元素的文本数组
SELECT regexp_matches('foobarbequebaz', 'barbeque');   
 regexp_matches 
----------------
 {barbeque}
(1 row)

--匹配b开头+若干非b字符,匹配结果分前后两组,即匹配到bar、beque ,但是不带g,所以只匹配了第一行
SELECT regexp_matches('foobarbequebazilbarfbonk', '(b[^b]+)(b[^b]+)');   
    regexp_matches    
--------------
 {bar,beque}
(1 rows)


--相比于上面的示例,带了g,则继续匹配,然后显示到第二行文本数组中
SELECT regexp_matches('foobarbequebazilbarfbonk', '(b[^b]+)(b[^b]+)', 'g');   
    regexp_matches    
--------------
 {bar,beque}
 {bazil,barf}
(2 rows)

--从文本中提取所有11位手机号,全局匹配,返回所有行
SELECT regexp_matches('13812345678,13987654321', '\d{11}', 'g');
 regexp_matches 
----------------
 {13812345678}
 {13987654321}
(2 rows)

--忽略大小写匹配,加参数i
SELECT regexp_matches('fooBarBeQuebaz', 'barbeque', 'i');    
 regexp_matches 
----------------
 {BarBeQue}
(1 row)


--不带i,则区分大小写,匹配为空
SELECT regexp_matches('fooBarBeQuebaz', 'barbeque');     
 regexp_matches 
----------------
(0 rows)

如果没有子查询,当regexp_matches函数没有匹配上时,不会输出表中的数据,这通常不是所需的返回结果,应避免这种写法,建议可使用regexp_substr(text,text)函数来实现相同的功能。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
SELECT * FROM tab;
 c1  | c2  
-----+-----
 dws | dws
(1 row)

SELECT c1, regexp_matches(c2, '(bar)(beque)') FROM tab;
 c1 | regexp_matches 
----+----------------
(0 rows)

SELECT c1, c2, (SELECT regexp_matches(c2, '(bar)(beque)')) FROM tab;
 c1  | c2  | regexp_matches 
-----+-----+----------------
 dws | dws | 
(1 row)

相关文档