2015-11-03 54 views
1



我写低于所存储的程序被移动到服务器(“DIR”文件夹)的一种读取逗号分隔的文件的目的,并且当脚本被执行,它基本上解析文件(.csv),并将数据分配给其各自的变量(xJOB_ID,xCTRL_ID,xACCT_SEC,xCREATEDON_DATE),以便我可以将数据插入到表中。

我在Windows 7环境中使用Oracle SQL Developer版本4.0.0.13。幸运的是,在我的头上敲了几下代码后,我的脚本没有任何问题。该文件的

如何优化子&instring或替代功能用于调试

实施例的格式:
1111,2,T,10/10/2000
2222,12345,U,10/10/2001
5555,123,S,10/10/1999

我的问题: 我发现了一个小的难度使用SUBSTRING & INSTRING功能来分析数据,并想知道我怎么能提高日e脚本,以便在需要调试的情况下,可以轻松解决未编写存储过程的人员的问题。

请让我知道,如果这是有道理的。我给了你整个脚本,以便你能够理解我正在努力完成的任务,并且为了调试目的我可以改进代码。

create or replace PROCEDURE SP_INSERT_INTO_TABLE(xFILE_NAME IN VARCHAR2) 

IS 


--UTL_FILE is an oracle package that allows you to read and write operating system files. 

TEXT_DATA UTL_FILE.FILE_TYPE; 

v_ROW_LENGTH NUMBER := 1024; 
v_TEXTSTRING VARCHAR2(4000); 
cLINE   VARCHAR2(100); 
xJOB_ID   NUMBER; 
xCTRL_ID  NUMBER; 
xACCT_SEC  VARCHAR2(1); 
xCREATEDON_DATE DATE; 
xCOUNT   NUMBER := 0; 

BEGIN 
    BEGIN 
    --Streams in the file data and assigns it to TEXT_DATA variable. 
    TEXT_DATA := UTL_FILE.FOPEN('DIR', xFILE_NAME, 'R', v_ROW_LENGTH);  
END; 

--Begin LOOP to get each line and assign to cLINE to extract, assign to each variable, and insert into the table 
LOOP  
    BEGIN 
     --Gets each string/line up to the line terminator 
     UTL_FILE.GET_LINE(TEXT_DATA, v_TEXTSTRING); 
    EXCEPTION 
     WHEN NO_DATA_FOUND THEN 
      EXIT; 
    END; 

    --Each line is assigned to the variable cLINE. 
    cLINE := v_TEXTSTRING; 

    --Begin to parse data using SUBSTRING and INSTRING functions 
    BEGIN   
     --Extracts string from cLINE position 1 up to the first occurrence, converts it to a number, and assigns it to the variable. 
     xJOB_ID   := TO_NUMBER(SUBSTR(cLINE, 1,INSTR(cLINE, ',', 1, 1)-1)); 

     --Extracts string from cLINE between the 1st and 2nd occurrence, converts it to a number, and assigns it to the variable. 
     xCTRL_ID  := TO_NUMBER(SUBSTR(cLINE, INSTR(cLINE, ',', 1, 1)+1, INSTR(cLINE, ',', 1,2)-INSTR(cLINE, ',', 1,1)-1)); 

     --Extracts string from cLINE between the 2nd and 3rd occurrence and assigns it to the variable. 
     xACCT_SEC  := SUBSTR(cLINE, INSTR(cLINE, ',', 1, 2) +1, INSTR(cLINE, ',', 1,3)-INSTR(cLINE, ',', 1,2) -1); 

     --Extracts string from cLINE after the last occurrence, converts it to a date, and assigns it the variable. 
     xCREATEDON_DATE := TO_DATE(SUBSTR(cLINE, INSTR(cLINE, ',', 1, 3)+1), 'MM/DD/YYYY'); 

     INSERT INTO TABLE(JOB_ID, CTRL_ID, ACCT_SEC, CREATEDON_DATE) 
     VALUES(xJOB_ID, xCTRL_ID, xACCT_SEC, xCREATEDON_DATE); 
     COMMIT; 

     --Counter to count the amount of inserts 
     xCOUNT := xCOUNT + 1; 

    EXCEPTION 
     --Exception to handle the conversion of a string to a NUMBER or value is longer than the declared length of the variable. 
     WHEN VALUE_ERROR THEN 
     NULL; 

    END;   
END LOOP; 

DBMS_OUTPUT.PUT_LINE('RECORDS INSERTED: ' || xCOUNT);  
UTL_FILE.FCLOSE(TEXT_DATA); 

END; 
+2

是否有你创建自己的解决方案的原因,而不是使用[SQL \ * Loader](https://docs.oracle.com/cd/E11882_01/server.112/e22490/ldr_concepts.htm)或者[外部表](https://docs.oracle.com/cd/E11882_01/server.112/e22490/et_concepts.htm)将文件加载到表中?该文件已存在于服务器上并位于已识别的目录中,因此外部表似乎是最简单的方法。 –

+0

@AlexPoole - 说实话,没有意识到SQL * Loader,不知道我将如何使用它,我确实想到了一个外部表,但是当我运行'SELECT * FROM EXTERNAL_TABLE'时,似乎遇到了问题.CSV文件(也许我会尽快解决这个问题,并将我的脑袋多一点)。我是否因使用SP而让自己的生活变得艰难?谢谢! – NewComer

+1

优秀点,@AlexPoole!外部桌子FTW! \ o /(插入变成insert into table_name(...)select ... from external_table_name;'这真的很容易调试......) – Boneist

回答

2

你可以使用REGEXP_SUBSTR代替,因为这只是需要一个函数调用,而不是一系列SUBSTR和INSTR调用,如:

(Gary_W提供):

declare 
    v_str varchar2(20) := 'a,,bcd'; 
    v_substr1 varchar2(10); 
    v_substr2 varchar2(10); 
    v_substr3 varchar2(10); 
begin 
    v_substr1 := regexp_substr(v_str, '([^,]*)(,|$)', 1, 1, NULL, 1); 
    v_substr2 := regexp_substr(v_str, '([^,]*)(,|$)', 1, 2, NULL, 1); 
    v_substr3 := regexp_substr(v_str, '([^,]*)(,|$)', 1, 3, NULL, 1); 
    dbms_output.put_line(v_substr1||':'||v_substr2||':'||v_substr3); 
end; 
/

a::bcd 

以上将使用具有空白部分的字符串,如演示。搜索模式已分为两组(即子表达式):[^,]*,|$

第一组说:任何不是逗号([^,])的字符出现0次或更多次(*)。

第二组说:逗号或行尾。

因此,整个模式正在寻找一组除逗号之外的任何字符,这些逗号可能存在​​也可能不存在,随后是逗号或行末。

regexp_substr中的最后一个参数表示我们要从搜索模式中选择第一个子表达式来显示 - 如果我们没有包含这个,那么您最终将显示逗号作为字符串被返回。


如果你肯定其中没有字符串的元素将永远是空,那么下面的工作:

declare 
    v_str varchar2(20) := 'a,123,bcd'; 
    v_substr1 varchar2(10); 
    v_substr2 varchar2(10); 
    v_substr3 varchar2(10); 
begin 
    v_substr1 := regexp_substr(v_str, '[^,]+', 1, 1); 
    v_substr2 := regexp_substr(v_str, '[^,]+', 1, 2); 
    v_substr3 := regexp_substr(v_str, '[^,]+', 1, 3); 
    dbms_output.put_line(v_substr1||':'||v_substr2||':'||v_substr3); 
end; 
/

a:123:bcd 

这只是寻找一个字符串的指定位置不是逗号的字符,比上一个示例中使用的搜索模式更容易挖掘(imho!),但是强度较差。

+0

警告!如果在列表中有一个NULL元素,那么当搜索如何解析列表时,碰巧是最常见的形式'[^,] +'的正则表达式不起作用!它会默默地返回错误的元素,导致返回错误的数据。尝试将'v_str'设置为''a,bcd',并查看结果。改为使用此调用(第四个参数是您想要的元素):''regexp_substr(v_str,'(。*?)(,| $)',1,1,NULL,1)'。在这里看到更多的信息:https://stackoverflow.com/questions/25648653/regex-to-select-nth-value-from-a-list-allowing-for-nulls/25652018#25652018 –

+0

感谢您的警告,@ Gary_W。明天我得看看这个!如果我使用你提到的模式,我仍然会得到逗号(例如:with v_str ='a ,, bcd'我从上面的过程中得到a:,::bcd的输出);是否有一种将它们作为正则表达式的一部分移除的好方法,而不必添加rtrim()? – Boneist

+0

我不知道,也许你有一个错字?我得到了''a:bcd:'',你发布的是原始代码。根据我的建议,我得到了'a :: bcd'。 –