如何基于DB2中的分隔符拆分字符串值

时间:2013-09-23 14:32:16

标签: split db2

如何在DB2中拆分字符串值?

例如,给定值:

CHG-FFH.

我想在短划线( - )上拆分,这会产生两个值:

CHG 
FFH. 

我尝试使用split函数,但它不是DB2中的函数。

任何帮助将不胜感激。

11 个答案:

答案 0 :(得分:20)

简答:

  

你需要找到的位置   分隔符,然后使用它作为起点的子串,以及计算的长度。

SELECT 
    SUBSTR('CHG-FFH', 1, LOCATE('-','CHG-FFH')-1) as FIRST_PART
  , SUBSTR('CHG-FFH', LOCATE('-','CHG-FFH')+1)   as SECOND_PART
FROM SYSIBM.SYSDUMMY1;

BONUS!如果您经常这样做,请创建一个用户定义的函数来动态执行此操作。这是一个example in a DB fiddle.

CREATE FUNCTION SPLITTER (input VARCHAR(4000), delimiter CHAR, part_number INTEGER)
      RETURNS VARCHAR(4000)
      LANGUAGE SQL
      READS SQL DATA
      NO EXTERNAL ACTION
      DETERMINISTIC
      RETURN 

with pos_info (first_pos, length) as (
select 
  case when part_number = 1 then 0
       else LOCATE_IN_STRING(input, delimiter,1, part_number-1, OCTETS)  
  end as first_pos, 

  case when part_number = 1 then LOCATE_IN_STRING(input, delimiter,1, part_number, OCTETS) - 1 
       when LOCATE_IN_STRING(input, delimiter,1, part_number, OCTETS) = 0
        and  LOCATE_IN_STRING(input, delimiter,1, part_number-1, OCTETS) = 0
       then 0
       when LOCATE_IN_STRING(input, delimiter,1, part_number, OCTETS) = 0
       then length(input) -  LOCATE_IN_STRING(input, '-',1, part_number - 1,    OCTETS)
       else LOCATE_IN_STRING(input, delimiter,1, part_number, OCTETS) -    LOCATE_IN_STRING(input, delimiter,1, part_number-1, OCTETS) - 1
  end as length
from sysibm.sysdummy1
)

select    
    substr(input, first_pos+1,length) as part
from pos_info;

或者,您可以看到不同的方法here at this answer: Split a VARCHAR in DB2 to retrieve a value inside


答案很长:

DB2和其他关系数据库不提供单个函数来完成此任务。

原因很可能是它不是一个隐含的标量函数。如果您的字符串中包含多个短划线,您是否要将其拆分为三个部分?四?因此,第一步是要注意您的数据是否确定 - 如果它具有您要拆分的特定数量的组件。在你的例子中,你有两个,所以我将从这个假设开始,然后评论你将如何处理其他情况。

场景:一个字符串值,其中两个组件用分隔符分隔

只有两个部分,你需要找到分隔符的位置,然后通过在子字符串函数中使用它之前和之后的位置来找到它之前和之后的子串。

  1. 定位分隔符的索引。
  2. LOCATE('-','CHG-FFH')
    

    注意: DB2提供了两个可用于此的函数: POSITION (或POSSTR)和 LOCATE < / em>(或LOCATE_IN_STRING)。 LOCATE 功能更强大,因为它允许您指定一个起始位置,如果您有多个分隔符,这将非常有用。

      使用分隔符索引
    1. SUBSTR
    2. 对于第一部分,从位置1开始子字符串,直到分隔符前面的字符(分隔符位置 - 1):

      SUBSTR('CHG-FFH', 1,LOCATE('-','CHG-FFH')-1) as FIRST_PART
      

      对于第二部分,在分隔符索引(分隔符位置+ 1)之后的位置开始子字符串,并获取字符串的其余部分:

       SUBSTR('CHG-FFH', LOCATE('-','CHG-FFH')+1) as SECOND_PART
      

      最终结果:

      SELECT 
          SUBSTR('CHG-FFH', 1,LOCATE('-','CHG-FFH')-1) as FIRST_PART
        , SUBSTR('CHG-FFH', LOCATE('-','CHG-FFH')+1) as SECOND_PART
      FROM SYSIBM.SYSDUMMY1;
      

      场景:包含由分隔符分隔的三个组件的字符串值

      使用与第一个方案相同的概念,但您必须确定第二个分隔符的索引。使用第一个分隔符的索引来指定起点:注意 LOCATE 允许指定起始位置:

      >>-LOCATE(search-string,source-string-+--------+-+--------------------+-)-><
                                            '-,start-' '-,--+-CODEUNITS16-+-'     
                                                            +-CODEUNITS32-+       
                                                            '-OCTETS------' 
      

      查找第二个分隔符:

      使用第一个分隔符的位置作为查找第二个分隔符的起点。

      LOCATE('-','CHG-FFH-EEE', LOCATE('-','CHG-FFH-EEE')+1)
      

      将其用作第二个和第三个值的SUBSTR点,并且您已全部设置。注意:对于第二个值,您必须使用两个分隔符位置来对值进行子串。

      最终结果:

      SELECT 
          SUBSTR('CHG-FFH-EEE', 1,LOCATE('-','CHG-FFH-EEE')-1) as FIRST_PART
        , SUBSTR('CHG-FFH-EEE', LOCATE('-','CHG-FFH-EEE')+1, LOCATE('-','CHG-FFH-EEE', LOCATE('-','CHG-FFH-EEE'))-1) as SECOND_PART
        , SUBSTR('CHG-FFH-EEE', LOCATE('-','CHG-FFH-EEE', LOCATE('-','CHG-FFH-EEE')+1)+1) as THIRD_PART
      FROM SYSIBM.SYSDUMMY1;
      

      您可以看到此策略在您的String中更多数量的分隔符将失控。

      场景:不确定分隔符数

      这是一个棘手的问题,最好通过存储过程来解决。通过以下方式思考:您希望解析后的数据如何从算法中获取,您将如何访问数据?数组在SQL中不是本机类型,但它们存在于存储过程中,因此当您从字符串中解析出所有部分时,您将如何处理数组?

      此处回答了解决此问题的一种方法:

      Split a VARCHAR in DB2 to retrieve a value inside

答案 1 :(得分:2)

这是我试过的,它取得了我有效的结果。因此与所有人分享。

select column_name, substr(column_name,1,locate('-',column_name)-1), 
substr(column_name,locate('-',column_name)+1,
length(substr(column_name,locate('-',column_name)+1))) from 
table_name where column_name is not null and column_name!='' 
and column_name like '%-%'

答案 2 :(得分:2)

之所以这么晚才回答,是因为它展示了实现目标的更为简单和通用的方法。它基于使用正则表达式的函数,甚至在提出问题时也可用。

要获取第N个令牌(示例中为第2个):

SELECT 
  COL
-- since 9.7 (2009)
, xmlcast(xmlquery('fn:tokenize($s, "-")[2]' passing COL as "s") as varchar(20)) as one
-- since 11.1
, REGEXP_SUBSTR(COL, '([^-]*)-?', 1, 2, '', 1) as two
FROM (VALUES 'CHG-FFH.', 'ABC-DEF-GH') TAB (COL);

结果是:

|COL       |ONE                 |TWO       |
|----------|--------------------|----------|
|CHG-FFH.  |FFH.                |FFH.      |
|ABC-DEF-GH|DEF                 |DEF       |

要标记字符串:

-- since 9.7 (2009)
SELECT TAB.COL, TOK.SEQ, TOK.TOKEN
FROM 
  (VALUES 'CHG-FFH.', 'ABC-DEF-GH') TAB (COL)
, XMLTABLE
(
  'for $id in tokenize($s, "-") return <i>{string($id)}</i>' PASSING TAB.COL AS "s"
  COLUMNS
    SEQ   FOR ORDINALITY
  , TOKEN VARCHAR(20) PATH '.'
) TOK
ORDER BY TAB.COL, TOK.SEQ;

结果是:

|COL       |SEQ                 |TOKEN               |
|----------|--------------------|--------------------|
|ABC-DEF-GH|1                   |ABC                 |
|ABC-DEF-GH|2                   |DEF                 |
|ABC-DEF-GH|3                   |GH                  |
|CHG-FFH.  |1                   |CHG                 |
|CHG-FFH.  |2                   |FFH.                |

答案 3 :(得分:1)

试试这句话:

select substr(your_value, 1,3), substr(your_value, 4, 3) from your_table

答案 4 :(得分:1)

这个答案并没有完全不同,但是它以更灵活的方式影响了LOCATE_IN_STRING,例如,如果您有限制,请不要使用函数。

要获取第n个以字符“-”分隔的字符串,如您的示例所示:

SUBSTR(THESTRING, LOCATE_IN_STRING(THESTRING, '-', 1,  n - 1) + 1, 
LOCATE_IN_STRING(THESTRING||'-', '-', 1,  n) - LOCATE_IN_STRING(THESTRING, '-', 1,  n - 1) - 1) 
                        

如果用所需的列号替换每次出现的 n ,则此构造将返回 n 列的内容。


一些解释:

要通过SUBSTR提取的字符串的长度是根据第 n 个和第(n + 1)个定界字符(-在这种情况下)。请注意,(n + 1)分隔符位置是根据THESTRING加上位于其末尾的额外分隔符计算得出的,以解决我们寻找最后一列而THESTRING不以分隔符结尾的情况

答案 5 :(得分:0)

如果您确定每个子字符串长度为3个字符,则可以尝试此代码,前提是TABLE1是一个至少有X行的表(在此示例中X = 10):

select rc, substr(string_to_split, (rc-1)*3+rc, 3) as result from
    (select row_number() over() as rc from TABLE1 fetch first 10 rows only) TB_rowcount
    cross join
    (select 'CHG-FFH' as string_to_split from sysibm.sysdummy1) T2
    where substr(string_to_split, (rc-1)*3+rc, 3) <> '   '

如果子串的长度不同,则必须应用LOCATE函数来查找分隔符

答案 6 :(得分:0)

我知道这是老帖子..但是想到以下可能会帮助别人。

我使用以下方法来分割给定的字符串。

SELECT TRIM(ITEM) AS ITEM FROM TABLE(<LIB1>.SF_SPLIT(I_INPUTLIST=>'AA|BB|CC|DD|EE|EE|FF', I_DELIMITER=>'|')) AS T;

SF_SPLIT is the User defined SQL function and below is definition:

CREATE OR REPLACE FUNCTION <LIB1>.SF_SPLIT(

    I_INPUTLIST VARCHAR(8000) 
  , I_DELIMITER VARCHAR(3)    

) 
RETURNS TABLE (ITEM VARCHAR(8000))

LANGUAGE SQL

RETURN

WITH R1 (ITEM, REMINDER) AS 

(SELECT SUBSTR(I_INPUTLIST, 1, LOCATE(I_DELIMITER, I_INPUTLIST)-1) AS ITEM, 

SUBSTR(I_INPUTLIST, LOCATE(I_DELIMITER, I_INPUTLIST)+1, LENGTH(I_INPUTLIST)) REMINDER

FROM SYSIBM.SYSDUMMY1

UNION ALL

SELECT SUBSTR(REMINDER, 1, LOCATE(I_DELIMITER, REMINDER)-1) AS ITEM, 
SUBSTR(REMINDER, LOCATE(I_DELIMITER, REMINDER)+1, LENGTH(REMINDER)) REMINDER 

FROM R1 WHERE LOCATE(I_DELIMITER, REMINDER) > 0

UNION ALL

SELECT SUBSTR(REMINDER, LOCATE(I_DELIMITER, REMINDER)+1, LENGTH(REMINDER)) AS ITEM,

'' AS REMINDER FROM R1 WHERE REMINDER <> '' AND LOCATE(I_DELIMITER, REMINDER) = 0

)

SELECT ITEM FROM R1;

答案 7 :(得分:0)

我需要使用instr,substr,trim和messed以及...但是instr和substr都是支持的。你可以找到一个模式。我必须通过' - '进行varchar分割,然后找到结束并从那里返回。

           select  itn, 
           substr(Message, 1 , locate(' - ', Message)) FIRST_SSR,  
           SUBSTR(Message , instr( message, ' - ', octets)+1, (instr( 
            message, '(Ref', octets)+1)) SECOND_STR ,
           Message
              from
         (
   select p.itn itn, 
          substr(p.msg,  instr( p.msg, ' - ' , octets)+21) Message
    from itnpad p
    where p.msg like '%MR - Multiple Requests%'

       ) A 

答案 8 :(得分:0)

在DB2中

SELECT
'11,222,33,444' AS THE_F_STRING
, SUBSTR('11,222,33,444', 1, LOCATE_IN_STRING('11,222,33,444',',',1,1)-1) AS AA
, SUBSTR('11,222,33,444', LOCATE_IN_STRING('11,222,33,444',',',1,1)+1, LOCATE_IN_STRING('11,222,33,444',',',1,2)-LOCATE_IN_STRING('11,222,33,444',',',1,1)-1) AS BB 
, SUBSTR('11,222,33,444', LOCATE_IN_STRING('11,222,33,444',',',1,2)+1, LOCATE_IN_STRING('11,222,33,444',',',1,3)-LOCATE_IN_STRING('11,222,33,444',',',1,2)-1) AS CC
, SUBSTR('11,222,33,444', LOCATE_IN_STRING('11,222,33,444',',',1,3)+1, LENGTH('11,222,33,444')-LOCATE_IN_STRING('11,222,33,444',',',1,3)) AS DD
FROM SYSIBM.SYSDUMMY1;

继续推断...享受......

答案 9 :(得分:0)

Suraj 的回答非常适合我,但我需要稍微更改长度逻辑。我现在也有冒号,以便在存储过程之外运行它。

<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" width="200px" height="50px" viewBox="0 0 2200 500" class="SignInButton">
    <style type="text/css">
        .fnt0 {
            font-size: 16rem;
            font-family: 'Segoe UI';
            text-decoration: none;
        }
    </style>
    <rect x="150" y="129" width="122" height="122" fill="#F35325" />
    <rect x="284" y="129" width="122" height="122" fill="#81BC06" />
    <rect x="150" y="263" width="122" height="122" fill="#05A6F0" />
    <rect x="284" y="263" width="122" height="122" fill="#FFBA08" />
    <text x="470" y="357" fill="white" class="fnt0"> Microsoft login</text>
</svg>

答案 10 :(得分:-1)

CREATE TYPE CUSTOMSTRINGARRAY AS VARCHAR(1000) ARRAY[VARCHAR(1000)];
create or replace function SPLIT_STRING(inputString varchar(1000),splitor varchar(10), pos int)
returns VARCHAR(1000)
ARRAYDEMO: BEGIN
DECLARE arraySymbols CUSTOMSTRINGARRAY;
DECLARE out_str,item,str VARCHAR(1000);
DECLARE i,occ INT;
SET i = 1;
set item = '';
set str = inputString;
set occ = LENGTH(inputString) - LENGTH(REPLACE(inputString,splitor,''));
WHILE i <= occ DO
set item = substr(str,1,LOCATE_IN_STRING(str,splitor,1));
set str = replace(str,item,'');
SET arraySymbols[i] = TRIM(replace(item,splitor,''));
SET i = i + 1;
END WHILE;
set arraySymbols[i] = str;
return(arraySymbols[pos]);
end;