2015-11-03 40 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'。 –