SQL Cookbook 系列 - 使用字符串

  1. 遍历字符串
  2. 字符串文字中包含引号
  3. 计算字符在字符串中出现的次数
  4. 从字符串中删除不需要的字符
  5. 将字符和数字数据分离
  6. 判别字符串是不是字母数字型的
  7. 提取名字的大写首字母缩写
  8. 按字符串中部分内容排序
  9. 安字符串中的数值排序
  10. 根据表中的行创建一个分隔列表
  11. 将分隔数据转换为多值in列表
  12. 按字母顺序排列字符串
  13. 判别可作为数值的字符串
  14. 提取第n个分隔的子串
  15. 分解IP地址

1.遍历字符串
遍历字符串在数据库中的开销是很大的,因为数据库中没有循环操作。
如果真的要这么做,可以参考:
select substr(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<=length(e.ename);
Note: t10,t100都是一种临时表,其id是从1开始逐渐递增的

2.字符串文字中包含引号
解决方案:通常出现在数据插入或者是条件查询的时候,
包含引号的字符串作为值或条件使用,参考如下:
select ename from emp where ename='Allen''s Khlid';
其中条件的值实际是 Allen's Khlid 。
Note:整个语句中单引号是成对出现的,最外层的单引号仅仅表示条件或值是字符串,
内层两个相邻单引号表示实际数据的一个单引号,如果在SQL中,两个单引号内没有任何值,
表示null值。例如: select ename from emp where ename='';
这个等价于 select ename from emp where ename is null;

3.计算字符在字符串中出现的次数
解决方案:用replace函数将制定字符串替换掉,计算与替换之前的长度差距来确定
select (length(ename)-length(replace(ename,'AC','')))/length('AC') as times from emp;

4.从字符串中删除不需要的字符
db2: select ename,replace(translate(ename,'AC','ac'),'ac','') as newone from emp;
mysql,sqlserver: select ename,replace(ename,'AC','') as newone from emp;
oracle,postgreSQL : select ename,replace(translate(ename,'AC','##'),'#','') as new one from emp;

Note:多用于进行批量操作,当然replace函数在where子句运行之前运行


5.将字符串和数字数据分离
db2: select replace(translate(data,'00000000000','0123456789'),'0','') ename,
cast(
replace(translate(lower(data),repeat('z',26),'abcdefghighklmnopqrstuvwxyz'),
'z','') as integer) sal from
(select ename||cast(sal as char(4)) data from emp) x;
oracle: select replace(translate(data,'0123456789','00000000000'),'0') ename,
to_number(
replace(translate(lower(data),'abcdefghighklmnopqrstuvwxyz',rpad('z',26,'z')),
'z')) sal from
(select ename||sal data from emp) x;
postgreSQL: select replace(translate(data,'00000000000','0123456789'),'0','') as ename,
cast(
replace(translate(lower(data),rpad('z',26,'z'),'abcdefghighklmnopqrstuvwxyz'),
'z','') as integer) as sal from
(select ename||sal as data from emp) x;
Note:看上去很麻烦的样子,确实很麻烦

6.判断字符串是不是字母数字型的
db2: select ename from emp where translate(lower(ename),repeat('a',36),'0123456789abcdefghijklmnopqrstuvwxyz') = repeat('a',length(ename));
mysql: select ename from emp where ename regexp '[0-9a-zA-Z]' >0;
oracle,postgreSQL : select ename from emp where translate(lower(ename),'0123456789abcdefghijklmnopqrstuvwxyz',rpad('a',36,'a'))=rpad('a',length(ename),'a');
sqlserver: 不支持translate函数,必须判断每一行中包含非数字字母值,回去看第1个就好

7.提取姓名的大写首字母缩写
可以使用substring函数或者是translate函数进行操作,每种数据库基本上问题不大

8.按字符串中的部分内容排序
这个就是按照字符串的子串进行排序
mysql : select ename from emp order by substr(ename,length(ename)-1,2);

9.按字符串中的数值排序
先将字符串中的数值转换成单独的一列,然后再按照这一列的值进行排序

10.根据表中的行创建一个分隔列表
db2 : with x (deptno,cnt,list,empno,len) as (
select deptno,count(*) over (partition by deptno),cast(ename as varchar(100)),empno,1 from emp union all
select x.deptno,x.cnt,x.list ||','|| e.ename,e.empno,x.len+1 from emp e,x
where e.deptno=x.deptno and e.empno>x.empno
) select deptno,list from x where len=cnt;
mysql: select deptno,group_concat(ename order by empno separator , ',') as emps from emp group by deptno;
oracle: select deptno,ltrim(sys_connect_by_path(ename,','),',') emps from
(selelct deptno,ename,row_number() over (partition by deptno order by empno) rn, count(*) over (partition by deptno) cnt from emp) where level=cnt
start with rn=1 connect by prior deptno=deptno and prior rn=rn-1;
postgreSQL和sqlserver也有,但是不再继续整理

11. 将分隔数据转换为多值IN列表
这个处理的是where子句中条件的数据类型不匹配问题
例如:select ename from emp where empno in ('1,9,16');
这种情况下是要想办法将后面的字符串转化成跟empno一致的数据类型才可以工作
出现几率非常小

12.按字母顺序排列字符串
db2: select ename, max(case when pos=1 then c else '' end )
|| max(case when pos=2 then c else '' end )
|| max(case when pos=3 then c else '' end )
|| max(case when pos=4 then c else '' end )
|| max(case when pos=5 then c else '' end )
|| max(case when pos=6 then c else '' end )
from (
select e.ename,cast(substr(e.ename,iter.pos,1) as varchar(100)) c,
cast(row_number() over (partition by e.name order by substr(e.ename,iter.pos,1)) as integer) pos
from emp e,
(select case(row_number() over ( ) as integer) pos from emp) iter
where iter.pos<=length(e.ename)
) x group by ename;
mysql: select ename,group_concat(c order by c separator '') from (
select ename,substr(ename,iter.pos,1) c from emp a,
(select id pos from t10) iter
where iter.pos<=length(a.ename)
) x group by ename;
oracle : select old_name,new_name from (
select old_name, replace(sys_connect_by_path(c, ' '),' ') new_name from (
select e.ename,old_name,row_number() over (partition by e.ename order by substr(e.ename,iter.pos,1) c from emp e,
(select rownum pos from emp) iter
where iter.pos<=length(e.ename) order by 1 ) x
start with rn=1 connect by prior rn=rn-1 and prior old_name=old_name
) where length(old_name) = length(new_name)
)
sqlserver: select ename, max(case when pos=1 then c else '' end )
+ max(case when pos=2 then c else '' end )
+ max(case when pos=3 then c else '' end )
+ max(case when pos=4 then c else '' end )
+ max(case when pos=5 then c else '' end )
+ max(case when pos=6 then c else '' end )
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 group by ename;


13.判别可作为数值的字符串
基本操作时使用函数replace和translate,然后做进一步处理

14.提取第n个分隔的子串
db2: select substr(c,2,locate(',',c,2)-2) from (
select pos,name,substr(name,pos) c,row_number() over (partition by name order by length(substr(name,pos)) desc) rn from (
select ',' ||csv.name|| ',' as name,cast(iter.pos as integer) as pos from v csv,
(select row_number() over () pos from t100) iter
where iter.pos<=length(csv.name)+2
) x where length(substr(name,pos))>1 and substr(substr(name,pos),1,1)=','
) y where rn=2;
mysql: select name from ( select iter.pos,substring_index(substring_index(src.name,',',iter.pos),',',-1) name from v src,
(select id pos from t10) iter where iter.pos<=length(src.name)-length(replace(src.name,',',''))) x where pos=2;
oracle: select sub from (
select iter.pos,src.name,substr(src.name,instr(src.name,',',1,iter.pos)+1,
instr(src.name,',',1,iter.pos+1)-
instr(src.name,',',1,iter.pos)-1 ) sub
from (select ','||name||',' as name from v) src,
(select rownum pos from emp) iter
where iter.pos<length(src.name)-length(replace(src.name,','))
) where pos=2;


15分解IP地址
这种分解过程可以从前面的实例中找到

Note:使用字符串操作要注意函数的运行时在where子句之前运行还是之后运行,当在where子句之前运行的时候,记录的行数直接决定了字符串操作的快慢。

猜你喜欢

转载自blog.csdn.net/seacean2000/article/details/80984850