How to split a string value based on a delimiter in DB2

后端 未结 10 1602
失恋的感觉
失恋的感觉 2020-11-27 08:09

How do you split a string value in DB2?

For example, given the value:

CHG-FFH.

I want to split on the dash (-), which would resul

相关标签:
10条回答
  • 2020-11-27 08:28

    I needed to use instr, substr, trim, and messed with locate as well.. but instr, and substr are all supported. You can find a pattern. I had to go through a varchar split with ' - ' and needed to find the end and go back from there.

               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 
    
    0 讨论(0)
  • 2020-11-27 08:30
    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;
    
    0 讨论(0)
  • 2020-11-27 08:33

    Short answer:

    You need to find the position of the delimiter, and then substring using it as the starting point, and a calculated length.

    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! If you do this often, create a user defined function to do it dynamically. Here's an 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;
    

    Alternatively, you can see a different approach here at this answer: Split a VARCHAR in DB2 to retrieve a value inside.


    Long answer:

    DB2, along with other relational databases do not provide a single function to accomplish this.

    The reason is likely that it's not an implicitly scalar function. If your string had more than one dash in it, would you want to split it into three parts? Four? So the first step is to note if your data is determinate - if it has a specific number of components that you want to split apart. In your example, you have two, so I'll start with that assumption, and then afterwards comment on how you would deal other situations.

    Scenario: A string value with two components separated by a delimiter

    With only two parts, you need to find the position of the delimiter, and then substring before and after it by using the position before and after it in a substring function.

    1. LOCATE the index of your delimiter.
    LOCATE('-','CHG-FFH')
    

    NOTE: DB2 provides two functions which can be used for this: POSITION (or POSSTR), and LOCATE (or LOCATE_IN_STRING). LOCATE is a bit more powerful because it allows you to specify a start position, which would be helpful if you had more than one delimiter.

    1. SUBSTR using the delimiter index.

    For the first part, start your substring at position 1, up to the character before the delimiter (delimiter position - 1):

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

    For the second part, start your substring at the position after delimiter index (delimiter position + 1), and get the rest of the String:

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

    Final Result:

    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;
    

    Scenario: A string value with three components separated by a delimiter

    Use the same concepts as the first scenario, but you have to determine the index of the second delimiter. Use the index of the first delimiter to specify a starting point: Note that LOCATE allows specifying a start position:

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

    Finding the second delimiter:

    Use the position of the first delimiter as the starting point for finding the second delimiter.

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

    Use that as a SUBSTR point for the second and third values, and you're all set. Note: For the second value, you have to use both of the delimiter locations to substring the value.

    Final Result:

    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;
    

    You can see this strategy would get out of hand for a greater number of delimiters in your String.

    Scenario: Indeterminate number of delimiters

    This is a tricky problem that is best approached with a Stored Procedure. Think through things like: How do you want the parsed data to come out of the algorithm, how will you access the data? Arrays are not a native type in SQL, but they are in Stored Procedures, so what will you do with the array when you've parsed all the pieces out of your String?

    One way to approach this scenario is answered here:

    Split a VARCHAR in DB2 to retrieve a value inside

    0 讨论(0)
  • 2020-11-27 08:40

    I know this is old post.. but thought following may help others.

    I used following approach to split the given string.

    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;
    
    0 讨论(0)
提交回复
热议问题