2015-07-16 14 views
9

Mam wartości zwracane z 255 oddzielonymi przecinkami wartościami. Czy istnieje prosty sposób na podzielenie ich na kolumny bez 255 substr?Podzielone wartości rozdzielone przecinkami do kolumn w Oracle

ROW | VAL 
----------- 
1 | 1.25, 3.87, 2, ... 
2 | 5, 4, 3.3, .... 

do

ROW | VAL | VAL | VAL ... 
--------------------- 
1 |1.25 |3.87 | 2 ...  
2 | 5 | 4 | 3.3 ... 
+0

dlaczego nie używać programu Excel? i skopiuj to z tego miejsca? – lookslikeanevo

Odpowiedz

10

Można użyć regexp_substr():

select regexp_substr(val, '[^,]+', 1, 1) as val1, 
     regexp_substr(val, '[^,]+', 1, 2) as val2, 
     regexp_substr(val, '[^,]+', 1, 3) as val3, 
     . . . 

chciałbym zasugerować wygenerowanie kolumnę 255 numerów w programie Excel (lub innym arkuszu kalkulacyjnym) i korzystać z arkusza kalkulacyjnego do generowania kodu SQL.

+1

To nie jest poprawna odpowiedź, ponieważ pytanie jawnie stwierdza "bez 255 substr". Pamiętaj również o odpowiedzi Gary_W, która jest bardzo ważna. – Wouter

+0

@Wouter. . . Punktem wyjścia było wygenerowanie wartości w arkuszu kalkulacyjnym. –

+0

Tak; sprytny. Ale wciąż nie jest odpowiedzią na to pytanie. – Wouter

14

Uwaga! Wyrażenie regexp_substr formatu '[^,]+' nie zwróci oczekiwanej wartości, jeśli na liście znajduje się element o wartości NULL i chcesz, aby ten element lub jeden po nim. Rozważmy następujący przykład, w którym 4. elementem jest NULL i chcę 5th Element, a zatem oczekiwać, że „5” do zwrotu:

SQL> select regexp_substr('1,2,3,,5,6', '[^,]+', 1, 5) from dual; 

R 
- 
6 

niespodzianka! Zwraca 5. element NIE-NULL, a nie faktyczny 5 element! Nieprawidłowe dane zostały zwrócone i może nawet nie można ich złapać. Spróbuj to zamiast:

SQL> select regexp_substr('1,2,3,,5,6', '(.*?)(,|$)', 1, 5, NULL, 1) from dual; 

R 
- 
5 

Tak, powyżej poprawione REGEXP_SUBSTR mówi szukać 5. występowania 0 lub więcej oddzielonych przecinkami znaków występuje przecinek lub końca linii (pozwala na następny separator, być to przecinek lub koniec linii) i po znalezieniu zwracają pierwszą podgrupę (dane NIE zawierają przecinka ani końca wiersza).

Wzór wyszukiwania mecz '(.*?)(,|$)' wyjaśnił:

(   = Start a group 
.    = match any character 
*    = 0 or more matches of the preceding character 
?    = Match 0 or 1 occurrences of the preceding pattern 
)    = End the 1st group 
(   = Start a new group (also used for logical OR) 
,    = comma 
|    = OR 
$    = End of the line 
)    = End the 2nd group 

EDIT: Więcej informacji dodanej i uproszczone regex.

Zobacz ten post, aby uzyskać więcej informacji i sugestii do hermetyzacji to w funkcji do łatwego ponownego użycia: REGEX to select nth value from a list, allowing for nulls To post gdzie odkryłem format '[^,]+' ma problemu. Niestety jest to format regex, który najczęściej jest odpowiedzią na pytania dotyczące analizowania listy. Drżę na myśl o wszystkich błędnych danych zwracanych przez '[^,]+'!

2

Jeśli masz tylko jeden wiersz, a czas, aby stworzyć swój


select * from (
    select rownum r , collection.* 
    from TABLE(cto_table(',','1.25, 3.87, 2, 19,, 1, 9, ')) collection 
) 
PIVOT ( 
    LISTAGG(column_value) within group (order by 1) as val 
    for r in (1, 2, 3, 4, 5, 6, 7, 8, 9, 10) 
) 

FYI: oto jak stworzyć cto_table funkcję:

CREATE OR REPLACE TYPE t_my_list AS TABLE OF VARCHAR2(100); 
CREATE OR REPLACE 
FUNCTION cto_table(p_sep in Varchar2, p_list IN VARCHAR2) 
    RETURN t_my_list 
AS 
    l_string VARCHAR2(32767) := p_list || p_sep; 
    l_sep_index PLS_INTEGER; 
    l_index PLS_INTEGER := 1; 
    l_tab t_my_list  := t_my_list(); 
BEGIN 
    LOOP 
    l_sep_index := INSTR(l_string, p_sep, l_index); 
    EXIT 
    WHEN l_sep_index = 0; 
    l_tab.EXTEND; 
    l_tab(l_tab.COUNT) := TRIM(SUBSTR(l_string,l_index,l_sep_index - l_index)); 
    l_index   := l_sep_index + 1; 
    END LOOP; 
    RETURN l_tab; 
END cto_table; 
/
1

hierarchiczne kwerendy mogą być wykorzystane. Obracanie można wykonać za pomocą case i group by.

with value_t as 
( 
    select row_t,row_number() OVER (partition by row_t order by rownum)rn, 
    regexp_substr(val, '[^,]+', 1, LEVEL) val from Table1 
CONNECT BY LEVEL <= regexp_count(val, '[^,]+') 
AND prior row_t = row_t 
AND prior sys_guid() is not null 
) select row_t, max(case when rn = 1 THEN val end) val_1, 
    max(case when rn = 2 THEN val end) val_2, 
    max(case when rn = 3 THEN val end) val_3 
    from value_t 
    group by row_t; 
Powiązane problemy