问题
遍历一个字符,并将其中的每个字符都作为一行返回,但是SQL没有循环操作。例如,要将表EMP中ENAME值为“KING”的字符串显示为4行,每行中都包含“KING”中的一个字符。
解决方案
使用笛卡儿积来生成行号,用来在该行中返回字符串中的每个字符。然后,使用DBMS中的内置的字符串分析函数来摘出所要显示的字符(SQL Server用户可使用SUBSTRING代替SUBSTR):
1 select substr(e.ename,iter.pos,1) as C
2 from (select ename from emp where ename = 'KING') e,
3 (select id as pos from t10) iter
4 where iter.pos <= length(e.ename)
C
-
K
I
N
G
讨论
逐一访问字符串中字符的关键是,所联接的表要有足够的行来得到所需要的反复次数。例中所用的表为T10,该表包含10行(该表有ID列,其中存储了数字1_10)。示例查询中能够返回的行数最大为10。
下面的例子显示了没有分解ENAME时在E和ITER(也就是说,指定的姓名和T10中这10行)的笛卡儿积:
select ename, iter.pos
from (select ename from emp where ename = 'KING') e,
(select id as pos from t10) iter
ENAME POS
---------- ----------
KING 1
KING 2
KING 3
KING 4
KING 5
KING 6
KING 7
KING 8
KING 9
KING 10
内联视图的基数为1,而内联视图ITER的基数为10,所以笛卡儿积为10行。生成这样的结果是在SQL中进行循环的第一步。
注意: 一般情况下,将表T10称为“基干1”表。
解决方案使用WHERE子句在返回4行之后,跳出循环。为限定结果集的行数与在姓名中所包含的字符数相同,所以在其中定义WHERE子句用了ITER.POS <= LENGTH(E.ENAME)作为条件:
select ename, iter.pos
from (select ename from emp where ename = 'KING') e,
(select id as pos from t10) iter
where iter.pos <= length(e.ename)
ENAME POS
---------- ----------
KING 1
KING 2
KING 3
KING 4
现在,对于在E.ENAME中的每个字符都有对应的一行,可以使用ER.POS作为SUBSTR的参数,在字符串中操作这些字符。ITER.PRS每行都在增加,这样,每行都会返回E.ENAME中的下一个字符。这也就是示例解决方案的工作原理。
根据所要实现的目标,可以决定是否需要对在字符串中每个字符都生成单独的一行。下面的例子是要遍历E.ENAME,并且显示字符串中的各个部分(超过1个字符):
select substr(e.ename,iter.pos) a,
substr(e.ename,length(e.ename)-iter.pos+1) b
from (select ename from emp where ename = 'KING') e,
(select id pos from t10) iter
where iter.pos <= length(e.ename)
A B
---------- ------
KING G
ING NG
NG ING
G KING
本章中的多数解决方案都有共同的部分,例如将整个字符串中的每个字符拆分为一行,或者根据字符串中特殊字符或分隔符的数量产生相应数量的行。