2016-06-21 29 views
2

我有這樣的字符串:選擇分隔的字符串作爲Oracle SQL表

「寬度:10 | 7 | 20 | 45,身高:25 | 5 | 6 | 45,長度:35 | 6 | 3 | 4"

我期待寫一個選擇查詢,選擇這像一個表:

Width | Height | Length 
----------------------- 
10 | 25 | 35 
7  | 5  | 6 
20 | 6  | 3 
45 | 45 | 4 

請評論,如果您需要任何更多的信息。

+0

您是否事先知道列的名稱或是否需要從字符串中動態提取它們? – MT0

回答

2

你是指這個?在您的oracle中運行它:

WITH TEST_DATA AS (
    SELECT 
     SUBSTR('Width:10|7|20|45,Height:25|5|6|45,Length:35|6|3|4', 
      INSTR('Width:10|7|20|45,Height:25|5|6|45,Length:35|6|3|4', 'Width:') + LENGTH('Width:'), 
      INSTR('Width:10|7|20|45,Height:25|5|6|45,Length:35|6|3|4', ',', 1) - LENGTH('Width:') - 1) AS WIDTH, 
     SUBSTR('Width:10|7|20|45,Height:25|5|6|45,Length:35|6|3|4', 
      INSTR('Width:10|7|20|45,Height:25|5|6|45,Length:35|6|3|4', 'Height:') + LENGTH('Height:'), 
      INSTR('Width:10|7|20|45,Height:25|5|6|45,Length:35|6|3|4', ',', INSTR('Width:10|7|20|45,Height:25|5|6|45,Length:35|6|3|4', 'Height:')) 
       - INSTR('Width:10|7|20|45,Height:25|5|6|45,Length:35|6|3|4', 'Height:') - LENGTH('Height:')) AS HEIGHT, 
     SUBSTR('Width:10|7|20|45,Height:25|5|6|45,Length:35|6|3|4', 
      INSTR('Width:10|7|20|45,Height:25|5|6|45,Length:35|6|3|4', 'Length:') + LENGTH('Length:')) AS LENGTH 
    FROM DUAL 
) 
SELECT 
    REGEXP_SUBSTR(WIDTH, '[^|]+', 1, ROWNUM) AS WIDTH, 
    REGEXP_SUBSTR(HEIGHT, '[^|]+', 1, ROWNUM) AS HEIGHT, 
    REGEXP_SUBSTR(LENGTH, '[^|]+', 1, ROWNUM) AS LENGTH 
FROM TEST_DATA 
CONNECT BY ROWNUM <= LENGTH(WIDTH)-LENGTH(REPLACE(WIDTH,'|'))+1 
3

此解決方案可以使用任意數量的列(寬度,高度...)和值。

-- your test data 
with data(val) as 
(select 'Width:10|7|20|45,Height:25|5|6|45,Length:35|6|3|4' from dual), 

-- split by , 
cols as 
(select regexp_substr(str, '[^,]+', 1, level) val 
    from (select val as str from data) 
    connect by regexp_substr((select val as str from data), 
          '[^,]+', 
          1, 
          level) is not null), 

-- split by : 
hdr_and_cols as 
(select substr(val, 1, instr(val, ':') - 1) as hdr, 
     substr(val, instr(val, ':') + 1) as val 
    from cols), 

-- split by | 
hdr_lvl_vals as 
(select distinct x.hdr, 
        level as entry, 
        regexp_substr(x.val, '[^|]+', 1, level) as val 
    from hdr_and_cols x 
    connect by regexp_substr(x.val, '[^|]+', 1, level) is not null) 

select * from hdr_lvl_vals; 

結果:

hdr  entry value 
--------------------- 
Height 1  25 
Height 2  5 
Height 3  6 
Height 4  45 
Length 1  35 
Length 2  6 
Length 3  3 
Length 4  4 
Width 1  10 
Width 2  7 
Width 3  20 
Width 4  45 

您可以格式化結果在你喜歡的例如方式

-- your test data 
with data(val) as 
(select 'Width:10|7|20|45,Height:25|5|6|45,Length:35|6|3|4' from dual), 

-- split by , 
cols as 
(select regexp_substr(str, '[^,]+', 1, level) val 
    from (select val as str from data) 
    connect by regexp_substr((select val as str from data), 
          '[^,]+', 
          1, 
          level) is not null), 

-- split by : 
hdr_and_cols as 
(select substr(val, 1, instr(val, ':') - 1) as hdr, 
     substr(val, instr(val, ':') + 1) as val 
    from cols), 

-- split by | 
hdr_lvl_vals as 
(select distinct x.hdr, 
        level as entry, 
        regexp_substr(x.val, '[^|]+', 1, level) as val 
    from hdr_and_cols x 
    connect by regexp_substr(x.val, '[^|]+', 1, level) is not null) 

-- format output 
select w.val as width, h.val as heigth, l.val as length 
    from (select entry, val from hdr_lvl_vals where hdr = 'Width') w, 
     (select entry, val from hdr_lvl_vals where hdr = 'Height') h, 
     (select entry, val from hdr_lvl_vals where hdr = 'Length') l, 
     (select level as entry 
      from dual 
     connect by level <= (select max(entry) from hdr_lvl_vals)) r 
where r.entry = w.entry 
    and r.entry = h.entry 
    and r.entry = l.entry; 

輸出:

WIDTH HEIGTH LENGTH 
-------------------- 
10  25  35 
7  5  6 
20  6  3 
45  45  4 
0

您可以使用集合和樞軸做到這一點:

甲骨文設置

小助手功能:

CREATE TYPE stringlist IS TABLE OF VARCHAR2(4000); 
/

CREATE FUNCTION split_String(
    i_str IN VARCHAR2, 
    i_delim IN VARCHAR2 DEFAULT ',' 
) RETURN stringlist DETERMINISTIC 
AS 
    p_result  stringlist := stringlist(); 
    p_start  NUMBER(5) := 1; 
    p_end   NUMBER(5); 
    c_len CONSTANT NUMBER(5) := LENGTH(i_str); 
    c_ld CONSTANT NUMBER(5) := LENGTH(i_delim); 
BEGIN 
    IF c_len > 0 THEN 
    p_end := INSTR(i_str, i_delim, p_start); 
    WHILE p_end > 0 LOOP 
     p_result.EXTEND; 
     p_result(p_result.COUNT) := SUBSTR(i_str, p_start, p_end - p_start); 
     p_start := p_end + c_ld; 
     p_end := INSTR(i_str, i_delim, p_start); 
    END LOOP; 
    IF p_start <= c_len + 1 THEN 
     p_result.EXTEND; 
     p_result(p_result.COUNT) := SUBSTR(i_str, p_start, c_len - p_start + 1); 
    END IF; 
    END IF; 
    RETURN p_result; 
END; 
/

查詢

WITH Data (value) AS (
    SELECT 'Width:10|7|20|45,Height:25|5|6|45,Length:35|6|3|4' FROM DUAL 
) 
SELECT * 
FROM (
    SELECT REGEXP_SUBSTR(c.COLUMN_VALUE, '^(.+?):(.+?)$', 1, 1, NULL, 1) AS name, 
     TO_NUMBER(r.COLUMN_VALUE) AS data, 
     ROW_NUMBER() OVER (PARTITION BY d.value, c.COLUMN_VALUE 
          ORDER BY ROWNUM 
          ) AS idx 
    FROM data d, 
     TABLE(split_String(d.value, ',')) c, 
     TABLE(split_String(
      REGEXP_SUBSTR(c.COLUMN_VALUE, '^(.+?):(.+?)$', 1, 1, NULL, 2), 
      '|' 
     )) r 
) PIVOT (
    MAX(data) FOR name IN ('Width' AS Width, 'Height' AS Height, 'Length' AS Length) 
) 
ORDER BY idx; 

(注:只有一個,你需要指定列名線)

輸出

IDX WIDTH HEIGHT LENGTH 
--- ----- ------ ------ 
    1 10  25  35 
    2  7  5  6 
    3 20  6  3 
    4 45  45  4 
0

只是爲了好玩with ora:tokenize

with w as (select 'Width:10|7|20|45,Height:25|5|6|45,Length:35|6|3|4' s from dual), 
t(s) as (select /*+ NO_XML_QUERY_REWRITE */ r from w, 
     xmltable('ora:tokenize(.,",")' 
        passing s 
      columns r varchar2(100) path '.')) 
select w.Width, w.Height, w.Length from(   
select /*+ NO_XML_QUERY_REWRITE */ dig,rn,regexp_substr(s,'\w+') as gr from t, 
     xmltable(' 
     for $c at $i in ora:tokenize(.,"\|") 
     return <r><dig>{$c}</dig><rn>{$i}</rn></r>' 
        passing regexp_replace(s,'\w+:') 
        columns 
      "DIG" varchar2(30) path '/r/dig', 
      "RN" varchar2(30) path '/r/rn' 
      )) 
     pivot 
     ( 
     max(dig) 
     for gr in('Width' as Width,'Height' as Height,'Length' as Length) 
    )w 
     ORDER BY RN