2010-10-23 75 views
21

Tôi đã chuỗi CSV 100.01,200.02,300.03 mà tôi cần phải chuyển sang một thủ tục được lưu trữ PL/SQL trong Oracle. Bên trong proc, tôi cần chèn các giá trị này vào cột Number trong bảng.Tách chuỗi phân tách bằng dấu phẩy trong một tệp được lưu trữ PL/SQL

Đối với điều này, tôi có một cách tiếp cận làm việc từ hơn ở đây:

How to best split csv strings in oracle 9i

[2) Sử dụng kết nối theo trình độ của SQL.].

Bây giờ, tôi có một yêu cầu khác. Tôi cần phải vượt qua 2 chuỗi CSV [bằng chiều dài] như đầu vào cho PL/SQL được lưu trữ proc.Và, tôi cần phải phá vỡ chuỗi này và chèn mỗi giá trị từ hai chuỗi CSV vào hai cột khác nhau trong bảng. tôi biết làm thế nào để đi về nó?

Ví dụ về đầu vào CSV: varchar2 bí ẩn (2000): = '0,75, 0,64, 0,56, 0,45';

myAmount varchar2 (2000): = '0,25, 0,5, 0,65, 0,8';

giá trị myString sẽ đi vào các giá trị Cột A và myAmount vào Cột B trong bảng.

Bạn có thể vui lòng cho tôi biết cách đạt được điều này không?

Cảm ơn.

+0

Khám phá http://nuijten.blogspot.com/2009/07/splitting-comma-delimited-strin g-regexp.html - bằng cách sử dụng biểu thức thông thường trong Oracle – InSane

Trả lời

7

Điều này sẽ làm những gì bạn đang tìm kiếm .. Nó giả định danh sách của bạn sẽ luôn là số. Nếu đó không phải là trường hợp, chỉ cần thay đổi các tham chiếu đến DBMS_SQL.NUMBER_TABLE đến một loại bảng mà làm việc cho tất cả các dữ liệu của bạn:

CREATE OR REPLACE PROCEDURE insert_from_lists(
    list1_in IN VARCHAR2, 
    list2_in IN VARCHAR2, 
    delimiter_in IN VARCHAR2 := ',' 
) 
IS 
    v_tbl1 DBMS_SQL.NUMBER_TABLE; 
    v_tbl2 DBMS_SQL.NUMBER_TABLE; 

    FUNCTION list_to_tbl 
    (
     list_in IN VARCHAR2 
    ) 
    RETURN DBMS_SQL.NUMBER_TABLE 
    IS 
     v_retval DBMS_SQL.NUMBER_TABLE; 
    BEGIN 

     IF list_in is not null 
     THEN 
      /* 
      || Use lengths loop through the list the correct amount of times, 
      || and substr to get only the correct item for that row 
      */ 
      FOR i in 1 .. length(list_in)-length(replace(list_in,delimiter_in,''))+1 
      LOOP 
       /* 
       || Set the row = next item in the list 
       */ 
       v_retval(i) := 
         substr (
          delimiter_in||list_in||delimiter_in, 
          instr(delimiter_in||list_in||delimiter_in, delimiter_in, 1, i ) + 1, 
          instr (delimiter_in||list_in||delimiter_in, delimiter_in, 1, i+1) - instr (delimiter_in||list_in||delimiter_in, delimiter_in, 1, i) -1 
         ); 
      END LOOP; 
     END IF; 

     RETURN v_retval; 

    END list_to_tbl; 
BEGIN 
    -- Put lists into collections 
    v_tbl1 := list_to_tbl(list1_in); 
    v_tbl2 := list_to_tbl(list2_in); 

    IF v_tbl1.COUNT <> v_tbl2.COUNT 
    THEN 
     raise_application_error(num => -20001, msg => 'Length of lists do not match'); 
    END IF; 

    -- Bulk insert from collections 
    FORALL i IN INDICES OF v_tbl1 
     insert into tmp (a, b) 
     values (v_tbl1(i), v_tbl2(i)); 

END insert_from_lists; 
7

Đây là một giải pháp tốt:

FUNCTION comma_to_table(iv_raw IN VARCHAR2) RETURN dbms_utility.lname_array IS 
    ltab_lname dbms_utility.lname_array; 
    ln_len  BINARY_INTEGER; 
BEGIN 
    dbms_utility.comma_to_table(list => iv_raw 
           ,tablen => ln_len 
           ,tab => ltab_lname); 
    FOR i IN 1 .. ln_len LOOP 
     dbms_output.put_line('element ' || i || ' is ' || ltab_lname(i)); 
    END LOOP; 
    RETURN ltab_lname; 
END; 

Nguồn: CSV - comma separated values - and PL/SQL (liên kết không còn giá trị)

+0

cảm ơn cho bình luận ur, nhưng, không chức năng này trả về một mảng? Nếu có, sau đó tôi sẽ có iterate qua nó trong proc để chèn các giá trị trong bảng. – Jimmy

+0

lname_array là một bảng: TYPE lname_array LÀ BẢNG VARCHAR2 (4000) INDEX BỞI BINARY_INTEGER; - Vì vậy, bạn chỉ có thể SELECT từ nó –

+1

một khởi đầu tốt nhưng cần thêm một chút công việc ở đây: Bạn không thể 'SELECT FROM' một loại mảng PL/SQL được khai báo trong một gói. Nếu bạn khai báo một kiểu bảng ở cấp lược đồ, bạn có thể chọn từ nó, nếu bạn sử dụng toán tử 'TABLE()'. –

3

Tôi không chắc chắn nếu điều này phù hợp với phiên bản oracle của bạn. Trên 10g của tôi, tôi có thể sử dụng chức năng bảng pipelined:

set serveroutput on 

create type number_list as table of number; 

-- since you want this solution 
create or replace function split_csv (i_csv varchar2) return number_list pipelined 
    is 
    mystring varchar2(2000):= i_csv; 
    begin 
    for r in 
    (select regexp_substr(mystring,'[^,]+',1,level) element 
     from dual 
    connect by level <= length(regexp_replace(mystring,'[^,]+')) + 1 
    ) 
    loop 
     --dbms_output.put_line(r.element); 
     pipe row(to_number(r.element, '999999.99')); 
    end loop; 
    end; 
/

insert into foo 
select column_a,column_b from 
    (select column_value column_a, rownum rn from table(split_csv('0.75, 0.64, 0.56, 0.45'))) a 
,(select column_value column_b, rownum rn from table(split_csv('0.25, 0.5, 0.65, 0.8'))) b 
where a.rn = b.rn 
; 
6

Tôi sử dụng apex_util.string_to_table để phân tích chuỗi, nhưng bạn có thể sử dụng trình phân tích cú pháp khác nếu bạn muốn. Sau đó, bạn có thể chèn dữ liệu như trong ví dụ này:

declare 
    myString varchar2(2000) :='0.75, 0.64, 0.56, 0.45'; 
    myAmount varchar2(2000) :='0.25, 0.5, 0.65, 0.8'; 
    v_array1 apex_application_global.vc_arr2; 
    v_array2 apex_application_global.vc_arr2; 
begin 

    v_array1 := apex_util.string_to_table(myString, ', '); 
    v_array2 := apex_util.string_to_table(myAmount, ', '); 

    forall i in 1..v_array1.count 
    insert into mytable (a, b) values (v_array1(i), v_array2(i)); 
end; 

Apex_util có sẵn từ Oracle 10G trở đi. Trước đó, nó được gọi là htmldb_util và không được cài đặt mặc định. Nếu bạn không thể sử dụng nó, bạn có thể sử dụng trình phân tích cú pháp chuỗi mà tôi đã viết nhiều năm trước và được đăng here.

+0

Cảm ơn bạn đã phản hồi.Nhưng bạn đang sử dụng Oracle 9i không hỗ trợ apex_util. – Jimmy

+0

OK, bạn có thể sử dụng bất kỳ chức năng "mã thông báo" nào giống như một trong liên kết tôi đã đăng hoặc một trong câu trả lời được chấp nhận cho câu hỏi liên quan bạn đã liên kết trong câu hỏi của bạn. Chỉ cần gọi nó một lần cho mỗi chuỗi được mã hóa. –

3
CREATE OR REPLACE PROCEDURE insert_into (
    p_errcode  OUT NUMBER, 
    p_errmesg  OUT VARCHAR2, 
    p_rowsaffected OUT INTEGER 
) 
AS 
    v_param0 VARCHAR2 (30) := '0.25,2.25,33.689, abc, 99'; 
    v_param1 VARCHAR2 (30) := '2.65,66.32, abc-def, 21.5'; 
BEGIN 
    FOR i IN (SELECT COLUMN_VALUE 
       FROM TABLE (SPLIT (v_param0, ','))) 
    LOOP 
     INSERT INTO tempo 
        (col1 
       ) 
      VALUES (i.COLUMN_VALUE 
       ); 
    END LOOP; 

    FOR i IN (SELECT COLUMN_VALUE 
       FROM TABLE (SPLIT (v_param1, ','))) 
    LOOP 
     INSERT INTO tempo 
        (col2 
       ) 
      VALUES (i.COLUMN_VALUE 
       ); 
    END LOOP; 
END; 
-1

Nhiều giải pháp tốt đã được cung cấp rồi. Tuy nhiên, nếu ông văn bản được cung cấp trong một định dạng phân dấu phẩy rất đơn giản hoặc tương tự, và tốc độ là quan trọng, sau đó tôi có cho bạn một giải pháp với một chức năng TABLE (trong PL/SQL). Tôi cũng đã cung cấp một loạt các giải pháp khác.

Vui lòng xem thêm trên Blog Entry on Parsing a CSV into multiple columns.

+0

Nếu bỏ phiếu xuống xảy ra, tôi sẽ đánh giá cao nhận xét. Giải pháp mà tôi cung cấp là kết quả của việc điều chỉnh và nghiên cứu hiệu suất rộng rãi. Nếu bạn tin rằng có điều gì đó sai hoặc xuyên tạc, tôi có thể có lợi khi biết lý do bỏ phiếu xuống là gì. – YoYo

+1

Tôi đã không bỏ phiếu, nhưng có lẽ vì câu trả lời này phụ thuộc rất nhiều vào một liên kết. –

-1

Đối với các trường hợp connect by sử dụng, phương pháp này sẽ làm việc cho bạn:

select regexp_substr('SMITH,ALLEN,WARD,JONES','[^,]+', 1, level) 
from dual 
connect by regexp_substr('SMITH,ALLEN,WARD,JONES', '[^,]+', 1, level) is not null; 
1

tạo hoặc thay thế procedure pro_ss(v_str varchar2) như

v_str1 varchar2(100); 
v_comma_pos number := 0;  
v_start_pos number := 1; 
begin    
    loop   
    v_comma_pos := instr(v_str,',',v_start_pos); 
    if v_comma_pos = 0 then  
     v_str1 := substr(v_str,v_start_pos); 
     dbms_output.put_line(v_str1);  
     exit; 
     end if;  
    v_str1 := substr(v_str,v_start_pos,(v_comma_pos - v_start_pos)); 
    dbms_output.put_line(v_str1);  
    v_start_pos := v_comma_pos + 1;  
    end loop; 
end; 
/

call pro_ss('aa,bb,cc,dd,ee,ff,gg,hh,ii,jj'); 

outout: aa bb cc đ ee ff gg hh ii jj

Các vấn đề liên quan