2017-06-13 117 views
0

我需要將csv字符串拆分爲n個部分。我有各種格式的地址,所以我將它們轉換爲csv字符串。我的報告需要3或4個地址行字段,所以我想將我的csv字符串拆分爲報告所需的部分數量。Oracle PL/SQL將csv字符串拆分爲n個部分

例如:

10,Dingle Apartments,MANZINI,,MANZINI,NGWANE STREET,,,Swaziland 

應分成等份3場:

1:10,Dingle Apartments,MANZINI 
2:,MANZINI,NGWANE STREET 
3:,,,Swaziland 

我寫了下面的代碼,但它不會在所有的工作很好:

-- ============================================================================ 
-- This function splits a csv string into x parts and returns one of the parts. 
-- Note: The smallest number of items in a part is hard coded to 2. Needs debug. 
-- 
-- p_string  - The csv string. 
-- p_parts   - The number of parts to split into. 
-- p_part   - The splitted part to return. 
-- p_separator  - The separator used in the csv string. 
-- p_separator_out - The separator to return. 
-- p_trim   - Trim trailing separator. Y/N 
-- ============================================================================ 
function get_csv_part 
(
    p_string  in varchar2, 
    p_parts   in number, 
    p_part   in number, 
    p_separator  in varchar2, 
    p_separator_out in varchar2, 
    p_trim   in varchar2 default 'Y' 
) return varchar2 is 

l_answer varchar2(32767) := ''; 
l_count number := 0; 
l_count2 number := 0; 
l_size number; 
l_pos number; 
l_pos2 number; 

begin 

    -- hr_utility.trace_on(null, 'FDL'); 
    hr_utility.trace('p_string: ' || p_string); 

    l_pos := instr(p_string, p_separator); 

    -- Determine the number of separators. 
    while l_pos > 0 loop 

     l_count := l_count + 1; 
     l_pos := instr(p_string, p_separator, l_pos + 1); 

    end loop; 

    -- Get the size of a part. 
    if l_count <= p_parts then 
     l_size := 2; 
    else 
     l_size := floor(l_count/p_parts); 
    end if; 

    --if l_size = 1 then 
    -- l_size := 2; 
    --end if; 

    hr_utility.trace('l_size: ' || to_char(l_size)); 

    l_pos := instr(p_string, p_separator); 

    if l_pos = 0 then 
     if p_part = 1 then 
     l_answer := p_string; 
     end if; 
    else 
     if p_part = 1 then 
     l_answer := substr(p_string, 1, l_pos - 1) || p_separator_out; 
     end if; 
    end if; 

    -- Split csv into parts. 
    while l_pos > 0 loop 

     l_count2 := l_count2 + 1; 
     l_pos2 := instr(p_string, p_separator, l_pos + 1); 

     hr_utility.trace('----------------------------------------'); 
     hr_utility.trace('l_count: ' || to_char(l_count)); 
     hr_utility.trace('l_count2: ' || to_char(l_count2)); 
     hr_utility.trace('floor(l_count2/l_size) + 1: ' || to_char(floor(l_count2/l_size) + 1)); 
     hr_utility.trace('l_pos: ' || to_char(l_pos)); 
     hr_utility.trace('l_pos2: ' || to_char(l_pos2)); 
     hr_utility.trace('l_answer: ' || l_answer); 

     -- If we are at a position that should go into the returned part. 
     if 
     (
     l_size > 1 
     and floor(l_count2/l_size) + 1 = p_part 
    ) 
     or 
     (
     l_size = 1 
     and l_count2 = p_part 
    ) 
     or 
     (
     p_part = p_parts 
     and l_size = 1 
     and l_count2 >= p_part 
    ) 
     or 
     (
     p_part = p_parts 
     and floor(l_count2/l_size) + 1 >= p_part 
    ) then 
     if l_pos2 = 0 then 
      if l_pos + 1 < length(p_string) then 
       l_answer := l_answer || substr(p_string, l_pos + 1) || p_separator_out; 
      end if; 
     elsif ((l_pos + 1) <= (l_pos2 - 1)) then 
      l_answer := l_answer || substr(p_string, l_pos + 1, ((l_pos2 - 1) - (l_pos + 1) + 1)) || p_separator_out; 
     else 
      l_answer := l_answer || p_separator_out; 
     end if; 
     end if; 

     l_pos := l_pos2; 

    end loop; 

    if p_part = p_parts then 

     l_pos := instr(p_string, p_separator, 1, l_count); 
     -- Dodge. 
     if instr(p_string, substr(p_string, l_pos + 1)) = 0 then 
     l_answer := l_answer || substr(p_string, l_pos + 1); 
     end if; 

    end if; 

    if p_trim = 'Y' then 

     -- Did not work if all separators. 
     -- l_answer := trim(trailing p_separator_out from l_answer); 
     if substr(l_answer, length(l_answer)) = p_separator_out then 
     l_answer := substr(l_answer, 1, length(l_answer) - 1); 
     end if; 

    end if; 

    return l_answer; 

end get_csv_part; 

部分問題是:

  • 它不爲1
  • 部分大小的工作它把地址部分爲多個部分,例如,,TEST,SWAZILIND,LOCATION,THING,,,,
  • 它將跳過地址份
  • 它使用不相等的部分的尺寸。

注意,由於某種原因,要求是有隔板離開空白字段,以便部分可能是,

誰能幫助我解決這個問題還是沒有人有能做到這樣的功能?

+0

您需要確保該地址組件不包含分隔符。當分隔符是可能的逗號時。我可以建議使用管道''|''而不是? –

+0

這正是我將分隔符作爲參數的原因。 – Superdooperhero

回答

0

未經測試,但喜歡的事:

CREATE OR REPLACE function get_csv_part(
    p_string  in varchar2, 
    p_parts   in number, 
    p_part   in number, 
    p_separator  in varchar2, 
    p_separator_out in varchar2, 
    p_trim   in varchar2 default 'Y' 
) 
RETURN VARCHAR2 DETERMINISTIC 
IS 
    p_value   VARCHAR2(4000) := p_string; 
    p_start   INTEGER; 
    p_end    INTEGER; 
    p_items   INTEGER; 
    p_sep_len CONSTANT INTEGER := LENGTH(p_separator); 
BEGIN 
    IF p_value IS NULL THEN 
    RETURN NULL; 
    END IF; 

    p_items = (LENGTH(p_value) - LENGTH(REPLACE(p_value, p_separator)))/p_sep_len + 1; 

    IF p_part = 1 THEN 
    p_start := 1; 
    ELSE 
    p_start := INSTR(p_value, p_separator, 1, ROUND((p_part - 1)/p_parts * p_items)) + p_sep_len; 
    END IF; 

    p_end := INSTR(p_value, p_separator, 1, ROUND(p_part/p_parts * p_items)) - 1; 

    IF p_end = -1 THEN 
    p_value := SUBSTR(p_value, p_start); 
    ELSE 
    p_value := SUBSTR(p_value, p_start, p_end - p_start + 1); 
    END IF; 

    IF p_trim = 'Y' THEN 
    WHILE SUBSTR(p_value, -p_sep_len) = p_separator THEN 
     p_value := SUBSTR(p_value, 1, LENGTH(p_value) - p_sep_len); 
    END LOOP; 
    END IF; 

    RETURN REPLACE(p_value, p_separator, p_separator_out); 
END; 
/
+0

我做了代碼編譯後,我測試了它,它不起作用。 – Superdooperhero

+1

你能澄清一下「不起作用」的含義嗎? – MT0

+0

,在Mlilwane,Manzini,Esitjeni路線中,Gables爲中間欄提供了一個空白欄。它也失去了山牆。 – Superdooperhero