Oracle PL / SQL将csv字符串拆分为n个部分

时间:2017-06-13 07:56:21

标签: oracle plsql

我需要将csv字符串拆分为n个部分。我有各种格式的地址,所以我将它们转换为csv字符串。我的报告需要3或4个地址行字段,因此我想将csv字符串拆分为报告所需的部分数量。

例如:

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,,,,
  • 跳过地址部分
  • 它使用不相等的零件尺寸。

请注意,出于某种原因,要求是将空白字段留给分隔符,以便部分可以是,

任何人都可以帮我解决这个问题,或者有没有人能够做到这一点?

1 个答案:

答案 0 :(得分: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;
/