13

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

例如,给定值:

CHG-FFH.

我想在破折号 (-) 上拆分,这将产生两个值:

CHG 
FFH. 

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

任何帮助将不胜感激。

4

11 回答 11

30

简短的回答:

您需要找到分隔符的位置,然后将其作为起点的子字符串,并计算出长度。

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;

奖金!如果您经常这样做,请创建一个用户定义的函数来动态执行此操作。这是 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;

或者,您可以在此答案中看到不同的方法:Split a VARCHAR in DB2 to retrieve a value inside


长答案:

DB2 以及其他关系数据库不提供单一功能来完成此任务。

原因很可能是它不是一个隐式的标量函数。如果你的字符串中有多个破折号,你想把它分成三部分吗?四?所以第一步是注意你的数据是否是确定的——如果它有你想要拆分的特定数量的组件。在您的示例中,您有两个,因此我将从该假设开始,然后评论您将如何处理其他情况。

场景:具有由分隔符分隔的两个组件的字符串值

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

  1. 找到分隔符的索引。
LOCATE('-','CHG-FFH')

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

  1. SUBSTR使用分隔符索引。

对于第一部分,从位置 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;

你可以看到这个策略会因为你的字符串中有更多的分隔符而失控。

场景:定界符的数量不定

这是一个棘手的问题,最好使用存储过程来解决。仔细考虑以下事项:您希望如何从算法中得出解析数据,您将如何访问数据?数组不是 SQL 中的原生类型,但它们存在于存储过程中,那么当您从 String 中解析出所有片段后,您将如何处理数组?

这里回答了处理这种情况的一种方法:

在 DB2 中拆分 VARCHAR 以检索其中的值

于 2013-09-23T16:00:43.637 回答
6

这是我尝试过的,它给我带来了有效的结果。因此与大家分享。

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 '%-%'
于 2013-09-25T13:58:51.077 回答
5

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

要获得第 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.                |
于 2020-05-01T10:38:25.030 回答
2

试试这个说法:

select substr(your_value, 1,3), substr(your_value, 4, 3) from your_table
于 2013-09-23T15:29:04.310 回答
2

这个答案并没有完全不同,但它以更灵活的方式实现 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 不以定界符结尾的情况。

于 2020-08-06T13:09:48.677 回答
1

我知道这是旧帖子..但认为以下可能对其他人有所帮助。

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

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;
于 2017-05-19T14:57:39.023 回答
1

在 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;

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

于 2017-11-24T03:49:07.457 回答
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函数来查找分隔符

于 2015-12-31T13:41:36.937 回答
0

我还需要使用 instr、substr、trim 并弄乱了定位。但是 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 
于 2017-06-29T22:41:19.703 回答
0

Suraj 的回答非常接近为我工作,但我需要稍微改变长度逻辑。此时我也有冒号,以便在存储过程之外运行它。

WITH R1 (ITEM, REMAINDER) 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) - LOCATE(:I_DELIMITER, :I_INPUTLIST)) REMAINDER

FROM SYSIBM.SYSDUMMY1
UNION ALL

SELECT SUBSTR(REMAINDER, 1, LOCATE(:I_DELIMITER, REMAINDER)-1) AS ITEM, 
SUBSTR(REMAINDER, LOCATE(:I_DELIMITER, REMAINDER)+1, LENGTH(:I_INPUTLIST) - LOCATE(:I_DELIMITER, :I_INPUTLIST)) REMAINDER 

FROM R1 WHERE LOCATE(:I_DELIMITER, REMAINDER) > 0

UNION ALL

SELECT SUBSTR(REMAINDER, LOCATE(:I_DELIMITER, REMAINDER)+1, LENGTH(:I_INPUTLIST) - LOCATE(:I_DELIMITER, :I_INPUTLIST)) AS ITEM,

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

)

SELECT ITEM FROM R1;
于 2021-06-25T16:21:23.340 回答
-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;
于 2019-03-11T12:39:45.707 回答