《SQL经典实例》第六章 处理字符串
在大多数情况下,你希望以每次穿过一个字符的方式来遍历字符串,可惜在 SQL 中,这种任务完成起来并不容易。由于 SQL 的循环功能有限,你需要通过模拟循环来遍历字符串。我们将这种操作称为“走查字符串”,本章的第一个实例对此做了介绍。使用 SQL 来分析字符串时,这种操作非常重要,本章的大部分实例参照并使用了它。强烈建议你弄清楚这种方法的工作原理。
--6.1 走查字符串
select substring(e.ename,iter.pos,1) as C
from (select ename from emp where ename = 'KING') e,
(select id as pos from t10) iter
where iter.pos <= datalength(e.ename)
内嵌视图 E 的基数为 1,而内嵌视图 ITER 的基数为 10,因此笛卡儿积为 10 行。要在 SQL 中模拟循环,首先需要生成这样的积。
--6.2 在字符串字面量中嵌入引号
select 'g''day mate' qmarks from t1
输出:g'day mate
SQL中的单引号即:'' (两个单引号) 单引号总是成对出现
--6.3 计算字符串中特定字符的出现次数
删除字符串中的逗号,再将原来的字符串长度与删除逗号后的字符串长度相减,就可以确定字符串中包含多少个逗号
select (datalength('10,CLARK,MAN,AGER')-
datalength(replace('10,CLARK,MANAGER',',','')))/datalength(',')
as cnt
from t1
第二行的除法,仅当要查找的字符串的长度大于 1 时,这个除法运算才是必不可少
--6.4 将不想要的字符从字符串中删除
SQL Server 2017中才新增了translate内置函数,低版本中运行会报错:'translate' 不是可以识别的 内置函数名称。
select ename,
replace(translate(ename,'aaaaa','AEIOU'),'a','') as stripped1,
sal,
replace(cast(sal as char(4)),'0','') as stripped2
from emp
--6.5 将数字数据和字符数据分开
SELECT REPLACE
( translate ( DATA, '0123456789', '0000000000' ), '0', '' ) AS ename,
cast(
REPLACE ( translate ( lower( DATA ), 'abcdefghijklmnopqrstuvwxyz', replicate ( 'z', 26 ), 'z', '' ) AS INTEGER ) AS sal
FROM
( SELECT concat( ename, sal ) AS DATA FROM emp ) x
--6.6 判断字符串是否只包含字母和数字
查找所有的字母数字字符。采取这种方法时,可以将所有的字母数字字符都转换为特定字符,从而将它们视为单个字符进行处理。为什么要这样做呢?这是因为这样可以将字母数字字符作为一个整体进行操作。将所有的字母数字字符都转换为你选择的字符后,将字母数字字符与其他字符隔离就易如反掌了。
SELECT DATA
FROM
V
WHERE
translate ( lower( DATA ), '0123456789abcdefghijklmnopqrstuvwxyz', replicate ( 'a', 36 ) ) = replicate ( 'a', len ( DATA ) )
replicate(char,length):SQL SERVER 内置函数,用char填充指定长度的字符串,并返回
--6.7 提取姓名中的首字母
解决思路:因为不如编程语言灵活,所以对字符串的格式要求比较严格。处理方法为先将姓名中的小写字母翻译为#,然后将#号去除,再将姓名中原有的空格替换成‘.’。
select replace(
replace(
translate(replace('Stewie Griffin', '.', ''),
'abcdefghijklmnopqrstuvwxyz',
replicate('#',26) ), '#','' ),' ','.' ) + '.'
from t1
--6.8 根据部分字符串排序
select ename
from emp
order by substring(ename,len(ename)-1,2)
--6.9 根据字符串中的数字排序
解决思路:先将字符串中的空格和字母替换成统一字符# 然后将将#号删除,再进行排序。使用DBMS的内置函数translate和replace函数实现。
SELECT DATA
FROM
V
ORDER BY
cast(
REPLACE (
translate (
DATA,
REPEAT
( '#', length( DATA ) ),
REPLACE ( translate ( DATA, '##########', '0123456789' ), '#', '' )
),
'#',
''
) AS INTEGER
)
--6.10 根据表中的行创建分隔列表
解决思路:SQL2017新增内置函数STRING_AGG(),进行字符串的分组和拼接。
SELECT
city,
STRING_AGG(email,';') email_list
FROM
sales.customers
GROUP BY
city;
--6.11 将分隔数据转换为多值in列表
SQL Server内置函数:Substring(data,startIndex,datalength) 截取字符串data 的从startIndex开始的datalength个字符。
SQL Server内置函数:charindex(char,data),查找字符串data中char所在得序号。
解决方法:
SELECT empno,
ename,
sal,
deptno
FROM emp
WHERE empno IN (SELECT Substring(c, 2, Charindex(',', c, 2) - 2) AS empno
FROM (SELECT Substring(csv.emps, iter.pos, Len(csv.emps)) AS c
FROM (SELECT ',' + '7654,7698,7782,7788' + ',' AS emps
FROM t1) csv,
(SELECT id AS pos
FROM t100) iter
WHERE iter.pos <= Len(csv.emps)) x
WHERE Len(c) > 1
AND Substring(c, 1, 1) = ',')
上面SQL得解决思路:通过将字符串放在两个逗号(分隔符)内,可以避免执行特殊的检查来确定字符串的起始位置或终止位置。下一步是只保留要在 IN 列表中使用的值。需要保留的是那些以逗号开头的行,但仅包含逗号的最后一行除外。使用函数 SUBSTR 或 SUBSTRING 找出以逗号开头的行,然后保留这些行中从开头的逗号到下一个逗号之间的所有字符。完成这项工作后,将得到的字符串转换为数字,以便与数值列 EMPNO(第 4~14 行)进行比较。
--6.12 按字母顺序排列字符串中得字符
解决方法:SQL Server 2017及以上版本使用string_agg()
select ename,STRING_AGG(c,'') rename
FROM (SELECT e.ename,
Substring(e.ename, iter.pos, 1)AS c,
Row_number() OVER (partition BY e.ename ORDER BY Substring(e.ename, iter.pos, 1)) AS pos
FROM emp e,
(SELECT Row_number()OVER(ORDER BY ename) AS pos
FROM emp) iter
WHERE iter.pos <= Len(e.ename)) x