• db2 将逗号分隔数据转换为多值IN列表


    将逗号分隔数据转换为多值IN列表  

    2010-03-15 11:16:59|  分类: 数据库技术|举报|字号 订阅

     
     

    原文:http://book.csdn.net/bookfiles/530/10053017906.shtml

    6.11       将分隔数据转换为多值IN列表

    问题

    已经有了分隔数据,想要将其转换为WHERE子句IN列表中的项目。考虑下面的字符串:

    7654,7698,7782,7788

    要将该字符串用在WHERE子句中,但是下面的SQL语句是错误的,因为EMPNO是一个数值列:

    select ename,sal,deptno

       from emp

      where empno in ( '7654,7698,7782,7788' )

    因为EMPNO是一个数值列,而此IN列表是一个字符串值,所以此SQL语句会失败。现要将此字符串转换为用逗号分解的数值列表。

    解决方案

    表面上看SQL应该将分隔字符串作为一个分隔值列表对待,但是实际情况不是这样。当SQL遇到括在引号中的逗号时,并不知道此符号表示多值列表,SQL必须将括在引号中的内容作为一个整体对待,也就是一个字符串值。因此必须将字符串分解为各个单独的EMPNO。这种解决方案的关键就是需要遍历字符串,但并不是一个字符一个字符地遍历,而是要将这个字符串转换为有效的EMPNO值。

    DB2

    通过遍历传递给IN列表的字符串,可以很轻松地将其转换为若干行。在这里函数ROW_NOMBER、LOCATE和SUBSTR尤其有用:

     1  select empno,ename,sal,deptno

     2    from emp

     3   where empno in (

     4  select cast(substr(c,2,locate(',',c,2)-2) as integer) empno

     5    from (

     6  select substr(csv.emps,cast(iter.pos as integer)) as c

     7    from (select ','||'7654,7698,7782,7788'||',' emps

     8            from t1) csv,

     9         (select id as pos

    10            from t100 ) iter

    11   where iter.pos <= length(csv.emps)

    12          )  x

    13    where length(c) > 1

    14      and substr(c,1,1) = ','

    15          )  y

    MySQL

    通过遍历传递给IN列别的字符串,可以很轻松地将其转换为若干行:

     1 select empno, ename, sal, deptno  

     2   from emp 

     3  where empno in       

     4        (

     5 select substring_index(

     6        substring_index(list.vals,',',iter.pos),',',-1) empno

     6   from (select id pos from t10) as iter,       

     7        (select '7654,7698,7782,7788' as vals

     8           from t1) list

     9  where iter.pos <=

    10        (length(list.vals)-length(replace(list.vals,',','')))+1

    11        )  x

    Oracle

    通过遍历传递给IN列别的字符串,可以很轻松地将其转换为若干行。这里函数ROWNUM,SUBSTR和INSTR尤其有用:

     1 select empno,ename,sal,deptno

     2   from emp

     3  where empno in (

     4        select to_number(

     5                   rtrim(

     6                  substr(emps,

     7                   instr(emps,',',1,iter.pos)+1,

     8                   instr(emps,',',1,iter.pos+1) -

     9                   instr(emps,',',1,iter.pos)),',')) emps

    10          from (select ','||'7654,7698,7782,7788'||',' emps from t1) csv,

    11               (select rownum pos from emp) iter

    12         where iter.pos <= ((length(csv.emps)-

    13                   length(replace(csv.emps,',')))/length(','))-1

    14  )

    Postgres

    通过遍历传递给IN列别的字符串,可以很轻松地将其转换为若干行。使用函数 SPLIT_PART可以简化将字符串解析为单独的数值列表的工作:

     1 select ename,sal,deptno

     2   from emp

     3  where empno in (

     4 select cast(empno as integer) as empno

     5   from (

     6 select split_part(list.vals,',',iter.pos) as empno

     7   from (select id as pos from t10) iter,

     8        (select ','||'7654,7698,7782,7788'||',' as vals

     9           from t1) list

    10  where iter.pos <=

    11        length(list.vals)-length(replace(list.vals,',',''))

    12        )  z

    13  where length(empno) > 0

    14        )  x

    SQL Server

    通过遍历传递给IN列别的字符串,可以很轻松地将其转换为若干行。这里函数ROW_NUMBER、CHARINDEX和SUBSTRING尤其有用:

     1 select empno,ename,sal,deptno

     2   from emp

     3  where empno in (select substring(c,2,charindex(',',c,2)-2) as empno

     4   from (

     5 select substring(csv.emps,iter.pos,len(csv.emps)) as c

     6   from (select ','+'7654,7698,7782,7788'+',' as emps

     7           from t1) csv,

     8        (select id as pos

     9          from t100) iter

    10  where iter.pos <= len(csv.emps)

    11        )  x

    12  where len(c) > 1

    13    and substring(c,1,1) = ','

    14        )  y

    讨论

    这种解决方案中第一步,也是最重要的一步就是遍历字符串。一旦完成了这步操作,剩下的操作就是使用DBMS提供的函数来将字符串解析为单独的数值。

    DB2和SQL Server

    内联视图X(第6~11行)遍历字符串,这里用的是“穿越”字符串的思想,所以其每一行都比其上一行少一个字符:

    ,7654,7698,7782,7788,

    7654,7698,7782,7788,

    654,7698,7782,7788,

    54,7698,7782,7788,

    4,7698,7782,7788,

    ,7698,7782,7788,

    7698,7782,7788,

    698,7782,7788,

    98,7782,7788,

    8,7782,7788,

    ,7782,7788,

    7782,7788,

    782,7788,

    82,7788,

    2,7788,

    ,7788,

    7788,

    788,

    88,

    8,

    ,

    注意,因为整个字符串是由逗号(分界符)括起来的,所以不需要特殊的检查来确定字符串从哪里开始及到哪里结束:

    下一步是只保留要用于内部列表中的值。除了最后只有单独逗号的一行之外,保留以逗号开头的行。使用SUBSTR或SUBSTRING函数来识别哪些行以逗号开头,并保留在此行中到下一个逗号之间的所有字符。此操作完成后,将字符串转换为数值,这样就可以用来正确地跟数值列EMPNO运算了(行4~14):

      EMPNO

     ------

       7654

       7698

       7782

       7788

    最后,使用在子查询中的结果来返回想要得到的行。

    MySQL

    内联视图(行5~9)遍历字符串。第10行的表达式通过查找逗号(分隔符)的数量来确定在字符串中有多少个值并加1。函数SUBSTRING_INDEX(第6行)返回在字符串中第n个逗号(分隔符)之前(到它左边)的所有字符:

    +---------------------+

    | empno               |

    +---------------------+

    | 7654                |

    | 7654,7698           |

    | 7654,7698,7782      |

    | 7654,7698,7782,7788 |

    +---------------------+

    这些行随后传递给SUBSTRING_INDEX(第5行)函数的另一次调用,此时第n个分隔符参数为-1,这表示在第n个分隔符右边所有的值需要保留。

    +-------+

    | empno |

    +-------+

    | 7654  |

    | 7698  |

    | 7782  |

    | 7788  |

    +-------+

    最后一步是将结果加入到一个子查询中。

    Oracle

    第一步是遍历字符串:

    select emps,pos

      from (select ','||'7654,7698,7782,7788'||',' emps

              from t1) csv,

           (select rownum pos from emp) iter

     where iter.pos <=

     ((length(csv.emps)-length(replace(csv.emps,',')))/length(','))-1

    EMPS                         POS

    --------------------- ----------

    ,7654,7698,7782,7788,          1

    ,7654,7698,7782,7788,          2

    ,7654,7698,7782,7788,          3

    ,7654,7698,7782,7788,          4

    有多少行返回,就表示在列表中有多少个值。POS值对于查询是至关重要的,因为要用它来将字符串分解为单独的值。使用SUBSTR和INSTR函数来分解字符串,POS用来确定在每个字符串中第n个分隔符的位置。由于字符串是用逗号括起来的,所以不需要特殊的检测手段来确定字符串的开始位置与结束位置。传递给SUBSTR,INSTR(7~9行)确定第n个和第n+1个分隔符的位置。用下一个逗号的位置值(字符串中下一个逗号的位置)减去当前逗号的位置值(字符串中当前逗号的位置),就可以从字符串中提取出每个值。

    select substr(emps,

           instr(emps,',',1,iter.pos)+1,

           instr(emps,',',1,iter.pos+1) -

           instr(emps,',',1,iter.pos)) emps

      from (select ','||'7654,7698,7782,7788'||',' emps

              from t1) csv,

           (select rownum pos from emp) iter

     where iter.pos <=

      ((length(csv.emps)-length(replace(csv.emps,',')))/length(','))-1 

     EMPS

     -----------

     7654,

     7698,

     7782,

     7788,

    最后一步就是删除每个值后面的逗号,将其转换为数值并且将其插入到子查询中。

    PostgreSQL

    内联视图Z(6~9行)遍历字符串,返回行的数量也就是在字符串中有多少值。要找出在字符串中有多少值,用带有分隔符的字符串长度减去不带分隔符的字符串长度即可(第9行),函数SPLIT_PART用来分解字符串,该函数查找第n个分隔符之前的值。

    SELECT列表.vals,

           split_part(list.vals,',',iter.pos) as empno,

           iter.pos

      from (select id as pos from t10) iter,

           (select ','||'7654,7698,7782,7788'||',' as vals

              from t1) list

     where iter.pos <=

           length(list.vals)-length(replace(list.vals,',',''))

             vals          | empno | pos

    -----------------------+-------+-----

     ,7654,7698,7782,7788, |       |   1

     ,7654,7698,7782,7788, | 7654  |   2

     ,7654,7698,7782,7788, | 7698  |   3

     ,7654,7698,7782,7788, | 7782  |   4

     ,7654,7698,7782,7788, | 7788  |   5

    最后一步就是将返回值(EMPNO)转换为数值,并且插入到子查询中。

  • 相关阅读:
    五大常用算法
    排序八 基数排序
    动态规划:从新手到专家(一)
    Linux Shell 文本处理工具集锦
    sleep与信号唤醒的问题 & 内核对信号的处理方式 & udelay
    《利用python进行数据分析》读书笔记--第九章 数据聚合与分组运算(一)
    《利用python进行数据分析》读书笔记--第八章 绘图和可视化
    《利用python进行数据分析》读书笔记--第七章 数据规整化:清理、转换、合并、重塑(三)
    《利用python进行数据分析》读书笔记--第七章 数据规整化:清理、转换、合并、重塑(二)
    k8s集群通过nginx-ingress做tcpudp 4层网络转发
  • 原文地址:https://www.cnblogs.com/yelongsan/p/7344243.html
Copyright © 2020-2023  润新知