将分隔的字符串拆分为Oracle中的行

时间:2014-11-09 14:35:25

标签: sql-server xml oracle

我有一个查询,我需要查看是否可以转换为Oracle

WITH Cte
     AS (SELECT cast('<S>' + replace(replace(N'$(AppServers)', ';', ','), ',', '</S><S>') + '</S>' AS XML) AS Servers)
INSERT INTO INSTANCE
            (INSTANCE_ID,
             SERVER_NAME,
             INSTANCE_IDENTIFIER,
             IDENTIFIER_PREFIX)
SELECT ROW_NUMBER() OVER (ORDER BY SERVER_NAME) - 1,
       SERVER_NAME,
       NULL,
       0
FROM   (SELECT DISTINCT upper(Split.Server.value('.', 'VARCHAR(100)')) AS SERVER_NAME
        FROM   Cte
               CROSS apply Servers.nodes('/S') Split(Server)) Servers
ORDER  BY SERVER_NAME; 

我知道Oracle 11g专门支持XML,但我无法找到解决这个问题的方法。非常感谢任何帮助。

如果$(AppServers)替换为foo,bar;baz,wibble,则上面的查询会插入以下内容。

+-------------+-------------+---------------------+-------------------+
| INSTANCE_ID | SERVER_NAME | INSTANCE_IDENTIFIER | IDENTIFIER_PREFIX |
+-------------+-------------+---------------------+-------------------+
|           0 | BAR         | NULL                |                 0 |
|           1 | BAZ         | NULL                |                 0 |
|           2 | FOO         | NULL                |                 0 |
|           3 | WIBBLE      | NULL                |                 0 |
+-------------+-------------+---------------------+-------------------+

XML代码就是将分隔(通过逗号或半冒号)字符串拆分为行。然后将值设置为大写并删除重复项,然后按字母顺序排序以获取Instance_Id。

2 个答案:

答案 0 :(得分:0)

经过一番研究,我设法复制最终结果(不是这样),如下所示:

DECLARE
  L_INPUT VARCHAR2(4000) := 'foo,bar,baz,wibble';
  L_COUNT BINARY_INTEGER;
  L_ARRAY DBMS_UTILITY.LNAME_ARRAY;
BEGIN
  DBMS_UTILITY.COMMA_TO_TABLE(LIST => REGEXP_REPLACE(L_INPUT, '(^|,)', '\1x'), TABLEN => L_COUNT, TAB => L_ARRAY);
  DBMS_OUTPUT.PUT_LINE(L_COUNT);
  FOR I IN 1 .. L_COUNT
  LOOP
    DBMS_OUTPUT.PUT_LINE('Element ' || TO_CHAR(I) || ' of array contains: ' || SUBSTR(L_ARRAY(I), 2));
    INSERT INTO INSTANCE VALUES (I, SUBSTR(L_ARRAY(I), 2), NULL, 0);
    COMMIT;
  END LOOP;
END;

一个问题是我不知道如何让它接受逗号,分号或更多字符作为分隔符(例如,Le pipe sign&#39; |&#39;)

答案 1 :(得分:0)

来自dbforums.com的@LKBrwn_DBA为此创建了一个解决方案:

CREATE OR REPLACE TYPE Csvparserreturn AS TABLE OF VARCHAR2 ( 4000 );
/

CREATE OR REPLACE FUNCTION Csvparser ( P_Dat IN CLOB, P_Hdr IN VARCHAR2 )
   RETURN Csvparserreturn
AS
   L_Debug        CHAR ( 1 ) := 'F';
   L_Hdr          VARCHAR2 ( 4000 );
   L_Dat          CLOB;
   L_Wrk          CLOB;
   L_Recsep       CHAR ( 1 ) := CHR ( 9 );

   O_Hdr          VARCHAR2 ( 4000 );
   O_Dat          CLOB;
   O_Data_Tab     Csvparserreturn := Csvparserreturn ( );

   TYPE Text_Array IS TABLE OF VARCHAR2 ( 4000 )
      INDEX BY PLS_INTEGER;

   L_Hdr_Array    Text_Array;
   L_Dat_Array    Text_Array;
   L_Fld_Array    Text_Array;
   L_Elm_Array    Text_Array;
   L_Val_Array    Text_Array;
   L_Hdr_Count    PLS_INTEGER;
   L_Dat_Count    PLS_INTEGER;
   L_Fld_Count    PLS_INTEGER;
   I              PLS_INTEGER;
   J              PLS_INTEGER;
   K              PLS_INTEGER;
   L              PLS_INTEGER;
   N              PLS_INTEGER;

   PROCEDURE Print_Line ( P_Text VARCHAR2 )
   IS
   BEGIN
      IF L_Debug = 'T'
      THEN
         DBMS_OUTPUT.Put_Line ( P_Text );
      END IF;
   END;

   FUNCTION Parse_Csv ( P_Text CLOB, P_Delim VARCHAR2 DEFAULT ',' )
      RETURN Text_Array
   IS
      Wk_Array       Text_Array;
   BEGIN
      N           := 1;
      L           := 1;
      K           := 1;

      WHILE 1 = 1
      LOOP
         L           := INSTR ( SUBSTR ( P_Text || P_Delim, K + 1 )
                              , P_Delim );
         EXIT WHEN K >= LENGTH ( P_Text );
         Wk_Array ( N ) := SUBSTR ( P_Text, K, L );
         N           := N + 1;
         K           := K + L + 1;
      END LOOP;

      RETURN Wk_Array;
   END;
BEGIN
   L_Hdr       := P_Hdr;
   L_Dat       := P_Dat;
   L_Hdr_Array := Parse_Csv ( L_Hdr );
   L_Hdr_Count := L_Hdr_Array.COUNT;
   Print_Line ( 'Hdr#' || L_Hdr_Count );
   O_Hdr       := '';

   FOR I IN 1 .. L_Hdr_Count
   LOOP
      O_Hdr       := O_Hdr || L_Hdr_Array ( I ) || L_Recsep;
      Print_Line ( 'Elm#' || I || ': ' || L_Hdr_Array ( I ) );
   END LOOP;

   L_Wrk       := SUBSTR ( L_Dat, 2, LENGTH ( L_Dat ) - 2 );

   SELECT REPLACE ( L_Wrk, '),(', L_Recsep ) || L_Recsep INTO L_Wrk FROM DUAL;

   Print_Line ( 'Dat: ' || L_Wrk );

   L_Dat_Array := Parse_Csv ( L_Wrk, L_Recsep );
   L_Dat_Count := L_Dat_Array.COUNT;


   O_Data_Tab.EXTEND;
   O_Data_Tab ( 1 ) := O_Hdr;

   FOR I IN 1 .. L_Dat_Count
   LOOP
      L_Fld_Array := Parse_Csv ( L_Dat_Array ( I ) );
      L_Fld_Count := L_Fld_Array.COUNT;
      Print_Line ( 'Rec#' || I || ': ' || L_Dat_Array ( I ) || ' Flds:' || L_Fld_Count );

      O_Dat       := '';

      FOR J IN 1 .. L_Hdr_Count
      LOOP
         K           := 0;

         FOR N IN 1 .. L_Fld_Count
         LOOP
            IF L_Hdr_Array ( J ) =
                  SUBSTR ( L_Fld_Array ( N )
                         , 1, LENGTH ( L_Hdr_Array ( J ) ) )
            THEN
               K           := N;
               CONTINUE;
            END IF;
         END LOOP;

         IF K > 0
         THEN
            L           := INSTR ( L_Fld_Array ( K ), '=' );
            L_Elm_Array ( J ) := SUBSTR ( L_Fld_Array ( K ), 1, L - 1 );
            L_Val_Array ( J ) := SUBSTR ( L_Fld_Array ( K ), L + 1 );
         ELSE
            L_Elm_Array ( J ) := '#N/A';
            L_Val_Array ( J ) := '#N/A';
         END IF;

         Print_Line ( 'Element ' || TO_CHAR ( J ) || '.' || L_Hdr_Array ( J ) || '=' || L_Val_Array ( J ) );

         O_Dat       := O_Dat || L_Val_Array ( J ) || L_Recsep;
      END LOOP;

      Print_Line ( 'Out#' || TO_CHAR ( I ) || '=' || O_Dat );

      O_Data_Tab.EXTEND;
      O_Data_Tab ( I + 1 ) := O_Dat;
   END LOOP;

   RETURN O_Data_Tab;
END;
/

以下是用法:

DECLARE
   Out_Data  Csvparserreturn := Csvparserreturn ( );
   X_Hdr     VARCHAR2 ( 4000 ) := 'Directory,ID,Location,UserName,Password,Selector';
   X_Dat     CLOB := '(Directory=Voice Active Directory A,ID=VT-AD1,Location=Canada,UserName=admin,Password=passw0rd,Selector=AD1),(Directory=Voice Active Directory B,ID=VT-AD2,Location=https://beta-proxy.voice.com/VTadp/Proxy/[/url],UserName=admin,Password=passw0rd,Selector=AD2),(Directory=Voice Active Directory C,ID=VT-AD3,Location=https://final-proxy.voice.com/VTadp/Proxy/,UserName=admin,Password=passw0rd)';
BEGIN
   Out_Data    := Csvparser ( X_Dat, X_Hdr );

   FOR I IN Out_Data.FIRST .. Out_Data.LAST
   LOOP
      DBMS_OUTPUT.Put_Line ( 'Rec# ' || TO_CHAR ( I, 'FM000.' ) || Out_Data ( I ) );
   END LOOP;
END;
/