การแยกสตริงออกเป็นหลายแถวใน Oracle


104

ฉันรู้ว่าสิ่งนี้ได้รับคำตอบในระดับหนึ่งกับ PHP และ MYSQL แต่ฉันสงสัยว่ามีใครสามารถสอนวิธีที่ง่ายที่สุดในการแยกสตริง (คั่นด้วยจุลภาค) เป็นหลายแถวใน Oracle 10g (ควรเป็น) และ 11g

ตารางมีดังนี้:

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

ฉันต้องการสร้างสิ่งต่อไปนี้:

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

ฉันได้เห็นโซลูชันที่เป็นไปได้สองสามข้อเกี่ยวกับสแต็ก แต่พวกเขาคิดเป็นคอลัมน์เดียวเท่านั้น (เป็นสตริงที่คั่นด้วยจุลภาค) ความช่วยเหลือใด ๆ ที่จะได้รับการชื่นชมอย่างมาก.


2
สำหรับตัวอย่างการใช้REGEXP, XMLTABLEและMODELข้อดู ลิตจุลภาคคั่นสตริงในตารางโดยใช้ Oracle SQL
Lalit Kumar B

คำตอบ:


121

นี่อาจเป็นวิธีที่ปรับปรุงแล้ว (เช่นเดียวกับ regexp และเชื่อมต่อโดย):

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

แก้ไข : นี่คือคำอธิบายง่ายๆ (เช่นเดียวกับ "ไม่เชิงลึก") ของข้อความค้นหา

  1. length (regexp_replace(t.error, '[^,]+')) + 1ใช้regexp_replaceเพื่อลบสิ่งที่ไม่ใช่ตัวคั่น (ในกรณีนี้ด้วยจุลภาค) และlength +1รับจำนวนองค์ประกอบ (ข้อผิดพลาด)
  2. select level from dual connect by level <= (...)ใช้แบบสอบถามลำดับชั้นในการสร้างคอลัมน์ที่มีจำนวนเพิ่มมากขึ้นในการแข่งขันที่พบจาก 1 ถึงจำนวนของข้อผิดพลาด

    ดูตัวอย่าง:

    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)) ทำการหล่อออราเคิลบางประเภท
    • การcast(multiset(.....)) as sys.OdciNumberListแปลงหลายคอลเลกชัน (หนึ่งคอลเลกชันสำหรับแต่ละแถวในชุดข้อมูลดั้งเดิม) เป็นคอลเลกชันเดียวของตัวเลข OdciNumberList
    • table()ฟังก์ชั่นแปลงคอลเลกชันลงใน resultset
  4. FROMโดยไม่ต้องเข้าร่วมจะสร้างการรวมข้ามระหว่างชุดข้อมูลของคุณและชุดข้อมูลหลายชุด ด้วยเหตุนี้แถวในชุดข้อมูลที่มีการจับคู่ 4 รายการจะทำซ้ำ 4 ครั้ง (โดยมีจำนวนเพิ่มขึ้นในคอลัมน์ชื่อ "column_value")

    ดูตัวอย่าง:

    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))ใช้column_valueเป็นnth_appearance / ocurrenceregexp_substrพารามิเตอร์
  6. คุณสามารถเพิ่มคอลัมน์อื่น ๆ จากชุดข้อมูลของคุณ ( t.name, t.projectตามตัวอย่าง) เพื่อให้เห็นภาพได้ง่าย

การอ้างอิงบางส่วนไปยัง Oracle Docs:


7
ระวัง! regex ของรูปแบบ'[^,]+'เพื่อแยกวิเคราะห์สตริงไม่ส่งคืนรายการที่ถูกต้องหากมีองค์ประกอบ null ในรายการ ดูข้อมูลเพิ่มเติมได้ที่นี่: stackoverflow.com/questions/31464275/…
Gary_W

13
ตั้งแต่ 11g คุณสามารถใช้regexp_count(t.error, ',')แทนlength (regexp_replace(t.error, '[^,]+'))ซึ่งอาจนำไปสู่การปรับปรุงประสิทธิภาพอีกครั้ง
Štefan Oravec

1
485 วินาทีโดย "ปกติ" CONNECT BY 0.296 วินาทีด้วยวิธีนี้ คุณ ROCK! ตอนนี้สิ่งที่ฉันต้องทำคือเข้าใจวิธีการทำงาน :-)
Bob Jarvis - คืนสถานะ Monica

@BobJarvis เพิ่มการแก้ไขเพื่ออธิบายว่ามันทำอะไร ยินดีต้อนรับการแก้ไขการสะกด / ไวยากรณ์
Nefreo

"คำตอบที่ยอมรับมีประสิทธิภาพต่ำ" - คำตอบที่ยอมรับในหัวข้อนี้คืออะไร? โปรดใช้ลิงก์เพื่ออ้างอิงโพสต์อื่น
0xdb

28

นิพจน์ทั่วไปเป็นสิ่งที่ยอดเยี่ยม :)

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
สวัสดีคุณช่วยชี้แจงฉันได้ไหมว่าทำไมข้อความค้นหาข้างต้นจึงให้แถวที่ซ้ำกันถ้าฉันไม่ได้ใช้คำหลักที่แตกต่างกันในการค้นหา
Jagadeesh G

2
ข้อความค้นหานั้นใช้ไม่ได้เนื่องจาก @JagadeeshG โดยเฉพาะอย่างยิ่งในตารางขนาดใหญ่
Michael-O

3
ช้ามากมีคำตอบที่ดีกว่าด้านล่าง
MoreCoffee

เหตุผลของการช้าคือว่าการรวมกันของทุกท่านมีการเชื่อมต่อซึ่งสามารถมองเห็นหากคุณลบName distinctแต่น่าเสียดายที่การเพิ่มand Name = prior Nameกับสาเหตุข้อconnect by ORA-01436: CONNECT BY loop in user data
MIK

คุณสามารถหลีกเลี่ยงORA-01436ข้อผิดพลาดได้โดยเพิ่มAND name = PRIOR name(หรือคีย์หลักอะไรก็ได้) และ AND PRIOR SYS_GUID() IS NOT NULL
David Faber

28

มีความแตกต่างอย่างมากระหว่างสองด้านล่าง:

  • แยกสตริงที่คั่นด้วยตัวเดียว
  • การแยกสตริงที่ใช้ตัวคั่นสำหรับหลายแถวในตาราง

หากคุณไม่ จำกัด แถวคำสั่งCONNECT BYจะสร้างหลายแถวและจะไม่ให้ผลลัพธ์ที่ต้องการ

นอกเหนือจากนิพจน์ทั่วไปแล้วยังมีทางเลือกอื่นอีกสองสามทางที่ใช้:

  • XMLTable
  • ส่วนคำสั่งMODEL

ติดตั้ง

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>

ใช้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>

การใช้MODEL clause:

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
คุณสามารถอธิบายเพิ่มเติมได้ไหมทำไมต้องมี('"' || REPLACE(text, ',', '","') || '"')และไม่สามารถลบวงเล็บออกได้ เอกสาร Oracle ([ docs.oracle.com/database/121/SQLRF/functions268.htm ) ไม่ชัดเจนสำหรับฉัน มันคือXQuery_stringอะไร?
Betlista

@ Betlista มันเป็นนิพจน์ XQuery
Lalit Kumar B

โซลูชัน XMLTABLE ด้วยเหตุผลบางประการล้มเหลวในการส่งออกรายการสุดท้ายสำหรับแถวที่มีความยาวแบบผสม เช่น. row1: 3 คำ; row2: 2 คำ, row3: 1 คำ; row4: 2 คำ, row5: 1 คำ - จะไม่ส่งออกคำสุดท้าย การจัดลำดับแถวไม่สำคัญ
Gnudiff

8

ตัวอย่างอีกสองสามตัวอย่างที่เหมือนกัน:

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
/

นอกจากนี้อาจใช้ 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


โปรดทราบว่าcomma_to_table()ใช้ได้เฉพาะกับโทเค็นที่เข้ากับหลักการตั้งชื่ออ็อบเจ็กต์ฐานข้อมูลของ Oracle มันจะเหวี่ยงสายเหมือน'123,456,789'เช่น
APC

7

ฉันต้องการเสนอแนวทางอื่นโดยใช้ฟังก์ชันตาราง PIPELINED ค่อนข้างคล้ายกับเทคนิคของ XMLTABLE ยกเว้นว่าคุณกำลังจัดเตรียมฟังก์ชันที่กำหนดเองเพื่อแยกสตริงอักขระ:

-- 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));

ผล:

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

ปัญหาเกี่ยวกับวิธีการประเภทนี้คือบ่อยครั้งที่เครื่องมือเพิ่มประสิทธิภาพจะไม่ทราบความสำคัญของฟังก์ชันตารางและจะต้องคาดเดา ซึ่งอาจเป็นอันตรายต่อแผนการดำเนินการของคุณดังนั้นจึงสามารถขยายโซลูชันนี้เพื่อให้สถิติการดำเนินการสำหรับเครื่องมือเพิ่มประสิทธิภาพ

คุณสามารถดูค่าประมาณของเครื่องมือเพิ่มประสิทธิภาพนี้ได้โดยการเรียกใช้แผนอธิบายในแบบสอบถามด้านบน:

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 |
----------------------------------------------------------------------------------------------

แม้ว่าคอลเล็กชันจะมีเพียง 3 ค่า แต่เครื่องมือเพิ่มประสิทธิภาพจะประมาณ 8168 แถวสำหรับมัน (ค่าเริ่มต้น) สิ่งนี้อาจดูเหมือนไม่เกี่ยวข้องในตอนแรก แต่อาจเพียงพอสำหรับเครื่องมือเพิ่มประสิทธิภาพในการตัดสินใจเลือกแผนย่อยที่เหมาะสมที่สุด

วิธีแก้ปัญหาคือการใช้ส่วนขยายเครื่องมือเพิ่มประสิทธิภาพเพื่อจัดทำสถิติสำหรับคอลเล็กชัน:

-- 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;

การทดสอบแผนการดำเนินการที่เป็นผลลัพธ์:

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 |
----------------------------------------------------------------------------------------------

ดังที่คุณเห็นคาร์ดินาลิตี้ในแผนด้านบนไม่ใช่ค่าที่เดา 8196 อีกต่อไป ยังไม่ถูกต้องเนื่องจากเรากำลังส่งคอลัมน์แทนที่จะเป็นสตริงลิเทอรัลไปยังฟังก์ชัน

การปรับแต่งโค้ดฟังก์ชันบางอย่างอาจจำเป็นเพื่อให้ประมาณค่าได้อย่างใกล้ชิดในกรณีนี้ แต่ฉันคิดว่าแนวคิดโดยรวมนั้นอธิบายได้ค่อนข้างดีที่นี่

ฟังก์ชัน str2tbl ที่ใช้ในคำตอบนี้พัฒนาโดย Tom Kyte: https://asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:110612348061

แนวคิดของการเชื่อมโยงสถิติกับประเภทออบเจ็กต์สามารถสำรวจเพิ่มเติมได้โดยอ่านบทความนี้: http://www.oracle-developer.net/display.php?id=427

เทคนิคที่อธิบายไว้ที่นี่ใช้งานได้ใน 10g +


4

ไม่ได้เพิ่ม REGEXP_COUNT จนกว่า Oracle 11i นี่คือโซลูชัน Oracle 10g ที่นำมาจากโซลูชันของ 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;

ฉันจะเพิ่มตัวกรองสำหรับสิ่งนี้ได้อย่างไรสมมติว่าฉันต้องการกรองด้วยชื่อเท่านั้น = '108' ฉันพยายามเพิ่มตำแหน่งที่อยู่หลังประโยคจาก แต่ลงท้ายด้วยรายการที่ซ้ำกัน
DRTauli

4

เริ่มจาก Oracle 12c คุณสามารถใช้JSON_TABLEและJSON_ARRAY:

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

และสอบถาม:

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;

เอาท์พุต:

┌──────┬─────────┬──────────────────┬──────┐
 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 <> การสาธิตซอ


1
ฉันยอมรับว่านี่เป็นเคล็ดลับที่ชาญฉลาด แต่ตรงไปตรงมามันจะทำให้ฉันสับสนถ้าฉันเจอมันในฐานรหัส
APC

@APC นี่เป็นเพียงการแสดงสิ่งที่เป็นไปได้กับ SQL ถ้าฉันต้องใช้รหัสดังกล่าวใน codebase ของฉันฉันจะห่อด้วยฟังก์ชันหรือแสดงความคิดเห็นเพิ่มเติม :)
Lukasz Szozda

แน่นอน. เพียงแค่ว่าเธรดนี้เป็นหนึ่งในเพลงยอดนิยมสำหรับการสร้างโทเค็นสตริงด้วย Oracle ดังนั้นฉันคิดว่าเราควรรวมข้อควรระวังเกี่ยวกับโซลูชันที่แปลกใหม่เพื่อปกป้องผู้บริสุทธิ์จากตัวเอง :)
APC

3

นี่คือการใช้งานทางเลือกอื่นโดยใช้ XMLTABLE ที่อนุญาตให้แคสต์ไปยังประเภทข้อมูลต่างๆ:

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

... หรือถ้าสตริงที่คั่นของคุณถูกเก็บไว้ในแถวหนึ่งหรือหลายแถวของตาราง:

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
;

ฉันคิดว่าโซลูชันนี้ใช้ได้กับ Oracle 11.2.0.3 และเวอร์ชันที่ใหม่กว่า
APC

2

ฉันต้องการเพิ่มวิธีอื่น อันนี้ใช้แบบสอบถามแบบวนซ้ำซึ่งฉันไม่เคยเห็นในคำตอบอื่น ๆ ได้รับการสนับสนุนโดย Oracle ตั้งแต่ 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

มันค่อนข้างยืดหยุ่นกับตัวละครแยก เพียงแค่เปลี่ยนในการINSTRโทร


2

โดยไม่ต้องใช้การเชื่อมต่อโดยหรือ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;


0

ใน Oracle 11g และใหม่กว่าคุณสามารถใช้แบบสอบถามย่อยแบบเรียกซ้ำและฟังก์ชันสตริงแบบง่าย (ซึ่งอาจเร็วกว่านิพจน์ทั่วไปและการสืบค้นย่อยแบบลำดับชั้นที่สัมพันธ์กัน):

การตั้งค่า 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;

คำถาม :

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

เอาท์พุต :

ชื่อ | โครงการ | ข้อผิดพลาด
---: | : ------ | : ----
 108 | ทดสอบ | ข้อผิดพลาด 1
 109 | test2 | ข้อผิดพลาด 1
 108 | ทดสอบ | Err2
 108 | ทดสอบ | Err3

db <> ซอที่นี่


-1

ฉันได้ใช้ฟังก์ชัน DBMS_UTILITY.comma_to _table ซึ่งใช้งานโค้ดได้ดังนี้

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; 

ฉันใช้ชื่อตารางและคอลัมน์ของตัวเอง


5
โปรดทราบว่าcomma_to_table()ใช้ได้เฉพาะกับโทเค็นที่เข้ากับหลักการตั้งชื่ออ็อบเจ็กต์ฐานข้อมูลของ Oracle มันจะเหวี่ยงสายเหมือน'123,456,789'เช่น
APC

เราสามารถใช้ตารางชั่วคราวได้หรือไม่?
Smart003

1
อืมเนื่องจากโซลูชันอื่น ๆ ที่ใช้งานได้ทั้งหมดทำไมเราถึงต้องการใช้ตารางชั่วคราวที่มาพร้อมกับค่าใช้จ่ายจำนวนมากในการทำให้ข้อมูลเป็นจริง?
APC
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.