Tách chuỗi thành nhiều hàng trong Oracle


104

Tôi biết điều này đã được giải đáp ở một mức độ nào đó với PHP và MYSQL, nhưng tôi đã tự hỏi liệu ai đó có thể dạy tôi cách tiếp cận đơn giản nhất để tách một chuỗi (được phân cách bằng dấu phẩy) thành nhiều hàng trong Oracle 10g (tốt hơn) và 11g hay không.

Bảng như sau:

Name | Project | Error 
108    test      Err1, Err2, Err3
109    test2     Err1

Tôi muốn tạo những thứ sau:

Name | Project | Error
108    Test      Err1
108    Test      Err2 
108    Test      Err3 
109    Test2     Err1

Tôi đã thấy một vài giải pháp tiềm năng xung quanh ngăn xếp, tuy nhiên chúng chỉ chiếm một cột duy nhất (là chuỗi được phân tách bằng dấu phẩy). Mọi sự trợ giúp sẽ rất được trân trọng.


2
Đối với ví dụ sử dụng REGEXP, XMLTABLEMODELkhoản, xem Chia dấu phẩy chuỗi phân định trong một bảng sử dụng Oracle SQL
Lalit Kumar B

Câu trả lời:


121

Đây có thể là một cách được cải thiện (cũng với regexp và kết nối bằng cách):

with temp as
(
    select 108 Name, 'test' Project, 'Err1, Err2, Err3' Error  from dual
    union all
    select 109, 'test2', 'Err1' from dual
)
select distinct
  t.name, t.project,
  trim(regexp_substr(t.error, '[^,]+', 1, levels.column_value))  as error
from 
  temp t,
  table(cast(multiset(select level from dual connect by  level <= length (regexp_replace(t.error, '[^,]+'))  + 1) as sys.OdciNumberList)) levels
order by name

CHỈNH SỬA : Đây là một giải thích đơn giản (như trong, "không chuyên sâu") về truy vấn.

  1. length (regexp_replace(t.error, '[^,]+')) + 1sử dụng regexp_replaceđể xóa bất kỳ thứ gì không phải là dấu phân cách (dấu phẩy trong trường hợp này) vàlength +1 để biết có bao nhiêu phần tử (lỗi).
  2. Sử select level from dual connect by level <= (...)dụng một truy vấn phân cấp để tạo cột có số lượng kết quả phù hợp ngày càng tăng được tìm thấy, từ 1 đến tổng số lỗi.

    Xem trước:

    select level, length (regexp_replace('Err1, Err2, Err3', '[^,]+'))  + 1 as max 
    from dual connect by level <= length (regexp_replace('Err1, Err2, Err3', '[^,]+'))  + 1
  3. table(cast(multiset(.....) as sys.OdciNumberList)) thực hiện một số đúc các loại oracle.
    • Phép cast(multiset(.....)) as sys.OdciNumberListbiến đổi nhiều tập hợp (một tập hợp cho mỗi hàng trong tập dữ liệu gốc) thành một tập hợp số duy nhất, OdciNumberList.
    • Các table()chức năng biến một bộ sưu tập thành một resultset.
  4. FROMkhông có phép nối sẽ tạo ra một phép nối chéo giữa tập dữ liệu của bạn và tập hợp đa. Kết quả là, một hàng trong tập dữ liệu có 4 khớp sẽ lặp lại 4 lần (với số lượng tăng dần trong cột có tên "giá_trị_cột").

    Xem trước:

    select * from 
    temp t,
    table(cast(multiset(select level from dual connect by  level <= length (regexp_replace(t.error, '[^,]+'))  + 1) as sys.OdciNumberList)) levels
  5. trim(regexp_substr(t.error, '[^,]+', 1, levels.column_value))sử dụng column_valuelàm tham số nth_appearance / ocention cho regexp_substr.
  6. Bạn có thể thêm một số cột khác từ tập dữ liệu của mình ( t.name, t.projectlàm ví dụ) để dễ hình dung.

Một số tham chiếu đến tài liệu Oracle:


7
Hãy coi chừng! Một regex của định dạng '[^,]+'để phân tích cú pháp chuỗi không trả về đúng mục nếu có phần tử null trong danh sách. Xem tại đây để biết thêm thông tin: stackoverflow.com/questions/31464275/…
Gary_W

13
kể từ 11g bạn có thể sử dụng regexp_count(t.error, ',') thay vì length (regexp_replace(t.error, '[^,]+')), có thể mang lại một cải thiện hiệu suất
Štefan Oravec

1
485 giây với CONNECT BY "bình thường". 0,296 giây theo cách này. Bạn ROCK! Bây giờ tất cả những gì tôi phải làm là hiểu nó hoạt động như thế nào. :-)
Bob Jarvis - Phục hồi Monica

@BobJarvis đã thêm một chỉnh sửa để giải thích chức năng của nó. Việc sửa lỗi chính tả / ngữ pháp được hoan nghênh.
Nefreo

"Câu trả lời được chấp nhận có hiệu suất kém" - câu trả lời được chấp nhận trong chủ đề này là gì? Vui lòng sử dụng các liên kết để tham khảo bài viết khác.
0xdb

28

biểu thức chính quy là một điều tuyệt vời :)

with temp as  (
       select 108 Name, 'test' Project, 'Err1, Err2, Err3' Error  from dual
       union all
       select 109, 'test2', 'Err1' from dual
     )

SELECT distinct Name, Project, trim(regexp_substr(str, '[^,]+', 1, level)) str
  FROM (SELECT Name, Project, Error str FROM temp) t
CONNECT BY instr(str, ',', 1, level - 1) > 0
order by Name

1
hi bạn có thể xin vui lòng làm rõ tôi tại sao các truy vấn trên cho hàng trùng lặp nếu tôi không sử dụng từ khóa riêng biệt trong truy vấn
Jagadeesh G

2
Truy vấn đó không sử dụng được do @JagadeeshG, đặc biệt là trên các bảng lớn.
Michael-O

3
Cực kỳ chậm, có câu trả lời tốt hơn bên dưới
MoreCoffee

Lý do của sự chậm chạp là mọi tổ hợp Names đều được kết nối với nhau, điều này có thể được nhìn thấy nếu bạn loại bỏ distinct. Không may thêm and Name = prior Namevào connect bymệnh đề nguyên nhân ORA-01436: CONNECT BY loop in user data.
mik

Bạn có thể tránh các ORA-01436lỗi bằng cách thêm AND name = PRIOR name(hoặc bất kỳ khóa chính có thể được) AND PRIOR SYS_GUID() IS NOT NULL
David Faber

28

Có một sự khác biệt lớn giữa hai điều dưới đây:

  • tách một chuỗi phân tách duy nhất
  • chia các chuỗi được phân cách cho nhiều hàng trong bảng.

Nếu bạn không hạn chế các hàng, thì mệnh đề CONNECT BY sẽ tạo ra nhiều hàng và sẽ không đưa ra kết quả mong muốn.

Ngoài Biểu thức chính quy , một số lựa chọn thay thế khác đang được sử dụng:

  • XMLTable
  • Mệnh đề MODEL

Thiết lập

SQL> CREATE TABLE t (
  2    ID          NUMBER GENERATED ALWAYS AS IDENTITY,
  3    text        VARCHAR2(100)
  4  );

Table created.

SQL>
SQL> INSERT INTO t (text) VALUES ('word1, word2, word3');

1 row created.

SQL> INSERT INTO t (text) VALUES ('word4, word5, word6');

1 row created.

SQL> INSERT INTO t (text) VALUES ('word7, word8, word9');

1 row created.

SQL> COMMIT;

Commit complete.

SQL>
SQL> SELECT * FROM t;

        ID TEXT
---------- ----------------------------------------------
         1 word1, word2, word3
         2 word4, word5, word6
         3 word7, word8, word9

SQL>

Sử dụng XMLTABLE :

SQL> SELECT id,
  2         trim(COLUMN_VALUE) text
  3  FROM t,
  4    xmltable(('"'
  5    || REPLACE(text, ',', '","')
  6    || '"'))
  7  /

        ID TEXT
---------- ------------------------
         1 word1
         1 word2
         1 word3
         2 word4
         2 word5
         2 word6
         3 word7
         3 word8
         3 word9

9 rows selected.

SQL>

Sử dụng mệnh đề MODEL :

SQL> WITH
  2  model_param AS
  3     (
  4            SELECT id,
  5                      text AS orig_str ,
  6                   ','
  7                          || text
  8                          || ','                                 AS mod_str ,
  9                   1                                             AS start_pos ,
 10                   Length(text)                                   AS end_pos ,
 11                   (Length(text) - Length(Replace(text, ','))) + 1 AS element_count ,
 12                   0                                             AS element_no ,
 13                   ROWNUM                                        AS rn
 14            FROM   t )
 15     SELECT   id,
 16              trim(Substr(mod_str, start_pos, end_pos-start_pos)) text
 17     FROM     (
 18                     SELECT *
 19                     FROM   model_param MODEL PARTITION BY (id, rn, orig_str, mod_str)
 20                     DIMENSION BY (element_no)
 21                     MEASURES (start_pos, end_pos, element_count)
 22                     RULES ITERATE (2000)
 23                     UNTIL (ITERATION_NUMBER+1 = element_count[0])
 24                     ( start_pos[ITERATION_NUMBER+1] = instr(cv(mod_str), ',', 1, cv(element_no)) + 1,
 25                     end_pos[iteration_number+1] = instr(cv(mod_str), ',', 1, cv(element_no) + 1) )
 26                 )
 27     WHERE    element_no != 0
 28     ORDER BY mod_str ,
 29           element_no
 30  /

        ID TEXT
---------- --------------------------------------------------
         1 word1
         1 word2
         1 word3
         2 word4
         2 word5
         2 word6
         3 word7
         3 word8
         3 word9

9 rows selected.

SQL>

1
Bạn có thể nói rõ hơn, tại sao phải có ('"' || REPLACE(text, ',', '","') || '"')và không thể bỏ dấu ngoặc? Tài liệu Oracle ([ docs.oracle.com/database/121/SQLRF/functions268.htm ) không rõ ràng đối với tôi. Là nó XQuery_string?
Betlista

@Betlista nó là một biểu thức XQuery.
Lalit Kumar B

Giải pháp XMLTABLE vì một số lý do liên tục không xuất được mục nhập cuối cùng cho các hàng có độ dài hỗn hợp. Ví dụ. row1: 3 từ; row2: 2 từ, row3: 1 từ; row4: 2 từ, row5: 1 từ - sẽ không xuất ra từ cuối cùng. Thứ tự các hàng không quan trọng.
Gnudiff

7

Một vài ví dụ khác tương tự:

SELECT trim(regexp_substr('Err1, Err2, Err3', '[^,]+', 1, LEVEL)) str_2_tab
  FROM dual
CONNECT BY LEVEL <= regexp_count('Err1, Err2, Err3', ',')+1
/

SELECT trim(regexp_substr('Err1, Err2, Err3', '[^,]+', 1, LEVEL)) str_2_tab
  FROM dual
CONNECT BY LEVEL <= length('Err1, Err2, Err3') - length(REPLACE('Err1, Err2, Err3', ',', ''))+1
/

Ngoài ra, có thể sử dụng DBMS_UTILITY.comma_to_table & table_to_comma: http://www.oracle-base.com/articles/9i/useful-procedures-and-functions-9i.php#DBMS_UTILITY.comma_to_table


Lưu ý rằng comma_to_table()chỉ hoạt động với các mã thông báo phù hợp với quy ước đặt tên đối tượng cơ sở dữ liệu của Oracle. '123,456,789'Ví dụ, nó sẽ ném vào một chuỗi .
APC

7

Tôi muốn đề xuất một cách tiếp cận khác bằng cách sử dụng hàm bảng PIPELINED. Nó hơi giống với kỹ thuật của XMLTABLE, ngoại trừ việc bạn đang cung cấp hàm tùy chỉnh của riêng mình để tách chuỗi ký tự:

-- Create a collection type to hold the results
CREATE OR REPLACE TYPE typ_str2tbl_nst AS TABLE OF VARCHAR2(30);
/

-- Split the string according to the specified delimiter
CREATE OR REPLACE FUNCTION str2tbl (
  p_string    VARCHAR2,
  p_delimiter CHAR DEFAULT ',' 
)
RETURN typ_str2tbl_nst PIPELINED
AS
  l_tmp VARCHAR2(32000) := p_string || p_delimiter;
  l_pos NUMBER;
BEGIN
  LOOP
    l_pos := INSTR( l_tmp, p_delimiter );
    EXIT WHEN NVL( l_pos, 0 ) = 0;
    PIPE ROW ( RTRIM( LTRIM( SUBSTR( l_tmp, 1, l_pos-1) ) ) );
    l_tmp := SUBSTR( l_tmp, l_pos+1 );
  END LOOP;
END str2tbl;
/

-- The problem solution
SELECT name, 
       project, 
       TRIM(COLUMN_VALUE) error
  FROM t, TABLE(str2tbl(error));

Các kết quả:

      NAME PROJECT    ERROR
---------- ---------- --------------------
       108 test       Err1
       108 test       Err2
       108 test       Err3
       109 test2      Err1

Vấn đề với kiểu tiếp cận này là thường trình tối ưu hóa sẽ không biết bản chất của hàm bảng và nó sẽ phải đoán. Điều này có thể có hại cho kế hoạch thực thi của bạn, vì vậy giải pháp này có thể được mở rộng để cung cấp số liệu thống kê thực thi cho trình tối ưu hóa.

Bạn có thể xem ước tính của trình tối ưu hóa này bằng cách chạy KẾ HOẠCH GIẢI THÍCH trên truy vấn ở trên:

Execution Plan
----------------------------------------------------------
Plan hash value: 2402555806

----------------------------------------------------------------------------------------------
| Id  | Operation                          | Name    | Rows  | Bytes | Cost (%CPU)| Time     |
----------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                   |         | 16336 |   366K|    59   (0)| 00:00:01 |
|   1 |  NESTED LOOPS                      |         | 16336 |   366K|    59   (0)| 00:00:01 |
|   2 |   TABLE ACCESS FULL                | T       |     2 |    42 |     3   (0)| 00:00:01 |
|   3 |   COLLECTION ITERATOR PICKLER FETCH| STR2TBL |  8168 | 16336 |    28   (0)| 00:00:01 |
----------------------------------------------------------------------------------------------

Mặc dù bộ sưu tập chỉ có 3 giá trị, trình tối ưu hóa đã ước tính 8168 hàng cho nó (giá trị mặc định). Điều này thoạt đầu có vẻ không liên quan, nhưng nó có thể đủ để trình tối ưu hóa quyết định cho một kế hoạch phụ tối ưu.

Giải pháp là sử dụng các tiện ích mở rộng của trình tối ưu hóa để cung cấp số liệu thống kê cho bộ sưu tập:

-- Create the optimizer interface to the str2tbl function
CREATE OR REPLACE TYPE typ_str2tbl_stats AS OBJECT (
  dummy NUMBER,

  STATIC FUNCTION ODCIGetInterfaces ( p_interfaces OUT SYS.ODCIObjectList )
  RETURN NUMBER,

  STATIC FUNCTION ODCIStatsTableFunction ( p_function  IN  SYS.ODCIFuncInfo,
                                           p_stats     OUT SYS.ODCITabFuncStats,
                                           p_args      IN  SYS.ODCIArgDescList,
                                           p_string    IN  VARCHAR2,
                                           p_delimiter IN  CHAR DEFAULT ',' )
  RETURN NUMBER
);
/

-- Optimizer interface implementation
CREATE OR REPLACE TYPE BODY typ_str2tbl_stats
AS
  STATIC FUNCTION ODCIGetInterfaces ( p_interfaces OUT SYS.ODCIObjectList )
  RETURN NUMBER
  AS
  BEGIN
    p_interfaces := SYS.ODCIObjectList ( SYS.ODCIObject ('SYS', 'ODCISTATS2') );
    RETURN ODCIConst.SUCCESS;
  END ODCIGetInterfaces;

  -- This function is responsible for returning the cardinality estimate
  STATIC FUNCTION ODCIStatsTableFunction ( p_function  IN  SYS.ODCIFuncInfo,
                                           p_stats     OUT SYS.ODCITabFuncStats,
                                           p_args      IN  SYS.ODCIArgDescList,
                                           p_string    IN  VARCHAR2,
                                           p_delimiter IN  CHAR DEFAULT ',' )
  RETURN NUMBER
  AS
  BEGIN
    -- I'm using basically half the string lenght as an estimator for its cardinality
    p_stats := SYS.ODCITabFuncStats( CEIL( LENGTH( p_string ) / 2 ) );
    RETURN ODCIConst.SUCCESS;
  END ODCIStatsTableFunction;

END;
/

-- Associate our optimizer extension with the PIPELINED function   
ASSOCIATE STATISTICS WITH FUNCTIONS str2tbl USING typ_str2tbl_stats;

Kiểm tra kế hoạch thực hiện kết quả:

Execution Plan
----------------------------------------------------------
Plan hash value: 2402555806

----------------------------------------------------------------------------------------------
| Id  | Operation                          | Name    | Rows  | Bytes | Cost (%CPU)| Time     |
----------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                   |         |     1 |    23 |    59   (0)| 00:00:01 |
|   1 |  NESTED LOOPS                      |         |     1 |    23 |    59   (0)| 00:00:01 |
|   2 |   TABLE ACCESS FULL                | T       |     2 |    42 |     3   (0)| 00:00:01 |
|   3 |   COLLECTION ITERATOR PICKLER FETCH| STR2TBL |     1 |     2 |    28   (0)| 00:00:01 |
----------------------------------------------------------------------------------------------

Như bạn có thể thấy cardinality trong kế hoạch ở trên không phải là giá trị được đoán 8196 nữa. Nó vẫn không chính xác vì chúng ta đang truyền một cột thay vì một chuỗi theo nghĩa đen cho hàm.

Một số điều chỉnh đối với mã chức năng sẽ là cần thiết để đưa ra một ước tính kỹ hơn trong trường hợp cụ thể này, nhưng tôi nghĩ rằng khái niệm tổng thể được giải thích khá nhiều ở đây.

Hàm str2tbl được sử dụng trong câu trả lời này ban đầu được phát triển bởi Tom Kyte: https://asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:110612348061

Có thể khám phá thêm khái niệm kết hợp thống kê với các kiểu đối tượng bằng cách đọc bài viết này: http://www.oracle-developer.net/display.php?id=427

Kỹ thuật được mô tả ở đây hoạt động trong 10g +.


4

REGEXP_COUNT chưa được thêm vào cho đến khi Oracle 11i. Đây là giải pháp Oracle 10g, được áp dụng từ giải pháp của Art.

SELECT trim(regexp_substr('Err1, Err2, Err3', '[^,]+', 1, LEVEL)) str_2_tab
  FROM dual
CONNECT BY LEVEL <=
  LENGTH('Err1, Err2, Err3')
    - LENGTH(REPLACE('Err1, Err2, Err3', ',', ''))
    + 1;

Làm cách nào tôi có thể thêm bộ lọc cho điều này, giả sử tôi muốn lọc chỉ với tên = '108'. Tôi đã thử thêm một where sau mệnh đề from nhưng cuối cùng vẫn có các từ trùng lặp.
DRTauli

4

Bắt đầu từ Oracle 12c, bạn có thể sử dụng JSON_TABLEJSON_ARRAY:

CREATE TABLE tab(Name, Project, Error) AS
SELECT 108,'test' ,'Err1, Err2, Err3' FROM dual UNION 
SELECT 109,'test2','Err1'             FROM dual;

Và truy vấn:

SELECT *
FROM tab t
OUTER APPLY (SELECT TRIM(p) AS p
            FROM JSON_TABLE(REPLACE(JSON_ARRAY(t.Error), ',', '","'),
           '$[*]' COLUMNS (p VARCHAR2(4000) PATH '$'))) s;

Đầu ra:

┌──────┬─────────┬──────────────────┬──────┐
 Name  Project       Error         P   
├──────┼─────────┼──────────────────┼──────┤
  108  test     Err1, Err2, Err3  Err1 
  108  test     Err1, Err2, Err3  Err2 
  108  test     Err1, Err2, Err3  Err3 
  109  test2    Err1              Err1 
└──────┴─────────┴──────────────────┴──────┘

db <> bản trình diễn fiddle


1
Tôi thừa nhận đây là một thủ thuật thông minh nhưng thành thật mà nói, nó sẽ đánh đố tôi nếu tôi bắt gặp nó trong một cơ sở mã.
APC

@APC Đây chỉ là màn trình diễn những gì có thể với SQL. Nếu tôi phải sử dụng mã như vậy trong codebase của tôi, tôi chắc chắn sẽ quấn nó với một chức năng hoặc để lại một bình luận mở rộng :)
Lukasz Szozda

Tất nhiên. Nó chỉ là chủ đề này là một trong những hit phổ biến hơn cho chuỗi tokenization với Oracle vì vậy tôi nghĩ chúng ta nên bao gồm hãy cẩn thận về những giải pháp kỳ lạ hơn, để bảo vệ sự ngây thơ từ bản thân :)
APC

3

Đây là một triển khai thay thế bằng cách sử dụng XMLTABLE cho phép truyền sang các kiểu dữ liệu khác nhau:

select 
  xmltab.txt
from xmltable(
  'for $text in tokenize("a,b,c", ",") return $text'
  columns 
    txt varchar2(4000) path '.'
) xmltab
;

... hoặc nếu các chuỗi được phân tách của bạn được lưu trữ trong một hoặc nhiều hàng của bảng:

select 
  xmltab.txt
from (
  select 'a;b;c' inpt from dual union all
  select 'd;e;f' from dual
) base
inner join xmltable(
  'for $text in tokenize($input, ";") return $text'
  passing base.inpt as "input"
  columns 
    txt varchar2(4000) path '.'
) xmltab
  on 1=1
;

Tôi nghĩ rằng giải pháp này hoạt động cho Oracle 11.2.0.3 và các phiên bản mới hơn.
APC

2

Tôi muốn thêm một phương pháp khác. Câu hỏi này sử dụng truy vấn đệ quy, điều mà tôi chưa thấy trong các câu trả lời khác. Nó được hỗ trợ bởi Oracle kể từ 11gR2.

with cte0 as (
    select phone_number x
    from hr.employees
), cte1(xstr,xrest,xremoved) as (
        select x, x, null
        from cte0
    union all        
        select xstr,
            case when instr(xrest,'.') = 0 then null else substr(xrest,instr(xrest,'.')+1) end,
            case when instr(xrest,'.') = 0 then xrest else substr(xrest,1,instr(xrest,'.') - 1) end
        from cte1
        where xrest is not null
)
select xstr, xremoved from cte1  
where xremoved is not null
order by xstr

Nó khá linh hoạt với ký tự phân tách. Đơn giản chỉ cần thay đổi nó trong các INSTRcuộc gọi.


2

Nếu không sử dụng kết nối bằng hoặc regexp :

    with mytable as (
      select 108 name, 'test' project, 'Err1,Err2,Err3' error from dual
      union all
      select 109, 'test2', 'Err1' from dual
    )
    ,x as (
      select name
      ,project
      ,','||error||',' error
      from mytable
    )
    ,iter as (SELECT rownum AS pos
        FROM all_objects
    )
    select x.name,x.project
    ,SUBSTR(x.error
      ,INSTR(x.error, ',', 1, iter.pos) + 1
      ,INSTR(x.error, ',', 1, iter.pos + 1)-INSTR(x.error, ',', 1, iter.pos)-1
    ) error
    from x, iter
    where iter.pos < = (LENGTH(x.error) - LENGTH(REPLACE(x.error, ','))) - 1;

1

Tôi đã gặp vấn đề tương tự và xmltable đã giúp tôi:

SELECT id, trim (COLUMN_VALUE) text FROM t, xmltable (('"' || REPLACE (text, ',', '", "') || '"'))


0

Trong Oracle 11g trở lên, bạn có thể sử dụng truy vấn phụ đệ quy và các hàm chuỗi đơn giản (có thể nhanh hơn các biểu thức chính quy và truy vấn phụ phân cấp có tương quan):

Thiết lập Oracle :

CREATE TABLE table_name ( name, project, error ) as
 select 108, 'test',  'Err1, Err2, Err3' from dual union all
 select 109, 'test2', 'Err1'             from dual;

Truy vấn :

WITH table_name_error_bounds ( name, project, error, start_pos, end_pos ) AS (
  SELECT name,
         project,
         error,
         1,
         INSTR( error, ', ', 1 )
  FROM   table_name
UNION ALL
  SELECT name,
         project,
         error,
         end_pos + 2,
         INSTR( error, ', ', end_pos + 2 )
  FROM   table_name_error_bounds
  WHERE  end_pos > 0
)
SELECT name,
       project,
       CASE end_pos
       WHEN 0
       THEN SUBSTR( error, start_pos )
       ELSE SUBSTR( error, start_pos, end_pos - start_pos )
       END AS error
FROM   table_name_error_bounds

Đầu ra :

TÊN | DỰ ÁN | LỖI
---: | : ------ | : ----
 108 | kiểm tra | Err1
 109 | test2 | Err1
 108 | kiểm tra | Err2
 108 | kiểm tra | Err3

db <> fiddle here


-1

tôi đã sử dụng hàm DBMS_UTILITY.comma_to _table trên thực tế, nó hoạt động mã như sau

declare
l_tablen  BINARY_INTEGER;
l_tab     DBMS_UTILITY.uncl_array;
cursor cur is select * from qwer;
rec cur%rowtype;
begin
open cur;
loop
fetch cur into rec;
exit when cur%notfound;
DBMS_UTILITY.comma_to_table (
     list   => rec.val,
     tablen => l_tablen,
     tab    => l_tab);
FOR i IN 1 .. l_tablen LOOP
    DBMS_OUTPUT.put_line(i || ' : ' || l_tab(i));
END LOOP;
end loop;
close cur;
end; 

tôi đã sử dụng tên bảng và cột của riêng mình


5
Lưu ý rằng comma_to_table()chỉ hoạt động với các mã thông báo phù hợp với quy ước đặt tên đối tượng cơ sở dữ liệu của Oracle. '123,456,789'Ví dụ, nó sẽ ném vào một chuỗi .
APC

chúng ta có thể thực hiện bằng cách sử dụng các bảng tạm thời?
Smart003

1
Umm, với tất cả các giải pháp khả thi khác, tại sao chúng ta lại muốn sử dụng các bảng tạm thời đi kèm với chi phí lớn để hiện thực hóa dữ liệu?
APC
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.