【问题标题】:Splitting a delimited string into rows in Oracle在Oracle中将分隔字符串拆分为行
【发布时间】:2014-11-16 16:29:09
【问题描述】:

我有一个查询需要查看是否可以转换为 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。

【问题讨论】:

  • 您的查询使用了很多特定的 SQL Server 语法。就目前而言,潜在的响应者需要了解 MSSQL、Oracle 和 XML。您将通过解释代码嵌套应该做什么来扩大潜在回答者的范围。
  • 我把它分解成小块,但你的建议是我害怕的:太多的 SQL Server 特定。

标签: sql-server xml oracle


【解决方案1】:

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

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 管道符号“|”)

【讨论】:

    【解决方案2】:

    来自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;
    /
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-11-17
      相关资源
      最近更新 更多