如何在 DB2 中拆分字符串值?
例如,给定值:
CHG-FFH.
我想在破折号 (-) 上拆分,这将产生两个值:
CHG
FFH.
我尝试使用 split 函数,但它不是 DB2 中的函数。
任何帮助将不胜感激。
简短的回答:
您需要找到分隔符的位置,然后将其作为起点的子字符串,并计算出长度。
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 以及其他关系数据库不提供单一功能来完成此任务。
原因很可能是它不是一个隐式的标量函数。如果你的字符串中有多个破折号,你想把它分成三部分吗?四?所以第一步是注意你的数据是否是确定的——如果它有你想要拆分的特定数量的组件。在您的示例中,您有两个,因此我将从该假设开始,然后评论您将如何处理其他情况。
场景:具有由分隔符分隔的两个组件的字符串值
只有两部分,您需要找到分隔符的位置,然后通过在子字符串函数中使用它之前和之后的位置,在它之前和之后子字符串。
LOCATE('-','CHG-FFH')
注意: DB2 提供了两个可用于此目的的函数:POSITION(或 POSSTR)和LOCATE(或 LOCATE_IN_STRING)。LOCATE功能更强大,因为它允许您指定起始位置,如果您有多个分隔符,这将很有帮助。
对于第一部分,从位置 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 中解析出所有片段后,您将如何处理数组?
这里回答了处理这种情况的一种方法:
这是我尝试过的,它给我带来了有效的结果。因此与大家分享。
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 '%-%'
这么晚回答的原因是为了展示更简单和通用的方法来实现目标。它基于使用正则表达式的函数,即使在提出问题时也可用。
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. |
试试这个说法:
select substr(your_value, 1,3), substr(your_value, 4, 3) from your_table
这个答案并没有完全不同,但它以更灵活的方式实现 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 不以定界符结尾的情况。
我知道这是旧帖子..但认为以下可能对其他人有所帮助。
我使用以下方法来拆分给定的字符串。
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;
在 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;
继续推断...享受...
如果您确定每个子字符串的长度为 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
函数来查找分隔符
我还需要使用 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
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;
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;