2013-07-16 12 views
6

Ho un campo indirizzo che può contenere 120 caratteri e deve dividerlo in tre colonne diverse 40 caratteri di lunghezza lunghi ciascuno.Come dividere la colonna varchar in Oracle in tre colonne

Esempio:

Table name: Address 
Column name: Street_Address 
Select Street_Address * from Address 

uscita: 123 Main St North Pole Factory 44, near the rear entrance cross the street and turn left and keep walking straight.

ho bisogno di dividere questo indirizzo nel address_1 address_2 e address_3.

Tutti e tre gli indirizzi sono il tipo di dati varchar(40).

Così risultato dovrebbe essere qualcosa di simile:

Address_1 
152 Main st North Pole Factory 44, near 

Address_2 
the rear entrance cross the street and 

Address_3 
turn left and keep walking straight. 

Si prega di notare che ogni campo di indirizzo può richiedere fino a 40 caratteri e deve essere l'intera parola, non può essere troncata a metà e lasciato privo di senso .

Sto utilizzando il database Oracle 11i.

+0

Dove mostrerai queste colonne? Perché non lo dividi a livello di applicazione? – bjan

risposta

1

Questo "abbastanza veloce e sporco", ma penso che dia i risultati corretti.
Ho usato un tavolo pipeline, ma probabilmente si può fare a meno ...

Here is a sqlfiddle demo

create table t1(id number, adr varchar2(120)) 
/
insert into t1 values(1, '152 Main st North Pole Factory 44, near the rear entrance cross the street and turn left and keep walking straight.') 
/
insert into t1 values(2, '122 Main st Pole Factory 44, near the rear entrance cross the street and turn left and keep walking straight. asdsa') 
/

create or replace type t is object(id number, phrase1 varchar2(40), phrase2 varchar2(40), phrase3 varchar2(40)) 
/
create or replace type t_tab as table of t 
/

create or replace function split_string(id number, str in varchar2) return t_tab 
    pipelined is 

    v_token varchar2(40); 
    v_token_i number := 0; 
    v_cur_len number := 0; 
    v_res_str varchar2(121) := str || ' '; 
    v_p1  varchar2(40); 
    v_p2  varchar2(40); 
    v_p3  varchar2(40); 
    v_p_i  number := 1; 

begin 

    v_token_i := instr(v_res_str, ' '); 

    while v_token_i > 0 loop 

    v_token := substr(v_res_str, 1, v_token_i - 1); 

     if v_cur_len + length(v_token) < 40 then 

     if v_p_i = 1 then 
      v_p1 := v_p1 || ' ' || v_token; 
     elsif v_p_i = 2 then 
      v_p2 := v_p2 || ' ' || v_token; 
     elsif v_p_i = 3 then 
      v_p3 := v_p3 || ' ' || v_token; 
     end if; 

     v_cur_len := v_cur_len + length(v_token) +1; 
    else 
     v_p_i := v_p_i + 1; 

     if v_p_i = 2 then 
      v_p2 := v_p2 || ' ' || v_token; 
     elsif v_p_i = 3 then 
      v_p3 := v_p3 || ' ' || v_token; 
     end if; 

     v_cur_len := length(v_token); 

    end if; 

    v_res_str := substr(v_res_str, v_token_i + 1); 
    v_token_i := instr(v_res_str, ' '); 

    end loop; 

    pipe row(t(id, v_p1, v_p2, v_p3)); 
    return; 
end split_string; 
/

e la query:

select parts.*, length(PHRASE1), length(PHRASE2), length(PHRASE3) 
from t1, table(split_string(t1.id, t1.adr)) parts 
+0

@ D.L, Ooops, dimenticato l'ultimo caso parola (dove non c'è spazio) ha aggiornato la mia risposta. (A proposito, c'è un altro modo per risolverlo - puoi inizializzare 'v_res_str varchar2 (120): = str || '';' –

+0

@DL, cosa intendi con * al volo *? Puoi usare la query suggerita come la vista (probabilmente senza i campi delle lunghezze ...) –

+0

@D.L, continuo a non vedere il problema: puoi aggiungere qualsiasi altra colonna. Vedi questo esempio http://www.sqlfiddle.com/#!4/eeb18/1 –

2

si potrebbe usare ricorsiva factoring subquery (CTE ricorsiva):

with s (street_address, line, part_address, remaining) as (
    select street_address, 0 as line, 
    null as part_address, street_address as remaining 
    from address 
    union all 
    select street_address, line + 1 as line, 
    case when length(remaining) <= 40 then remaining else 
     substr(remaining, 1, instr(substr(remaining, 1, 40), ' ', -1, 1)) end 
     as part_address, 
    case when length(remaining) <= 40 then null else 
     substr(remaining, instr(substr(remaining, 1, 40), ' ', -1, 1) + 1) end 
     as remaining 
    from s 
) 
cycle remaining set is_cycle to 'Y' default 'N' 
select line, part_address 
from s 
where part_address is not null 
order by street_address, line; 

Quale spirito i tuoi dati:

 LINE PART_ADDRESS       
---------- ---------------------------------------- 
     1 152 Main st North Pole Factory 44, near 
     2 the rear entrance cross the street and 
     3 turn left and keep walking straight.  

SQL Fiddle demo con due indirizzi.

Puoi anche convertire quei valori parziali in colonne, che penso sia il tuo obiettivo finale, ad es. come vista:

create or replace view v_address as 
with cte (street_address, line, part_address, remaining) as (
    select street_address, 0 as line, 
    null as part_address, street_address as remaining 
    from address 
    union all 
    select street_address, line + 1 as line, 
    case when length(remaining) <= 40 then remaining else 
     substr(remaining, 1, instr(substr(remaining, 1, 40), ' ', -1, 1)) end 
     as part_address, 
    case when length(remaining) <= 40 then null else 
     substr(remaining, instr(substr(remaining, 1, 40), ' ', -1, 1) + 1) end 
     as remaining 
    from cte 
) 
cycle remaining set is_cycle to 'Y' default 'N' 
select street_address, 
    cast (max(case when line = 1 then part_address end) as varchar2(40)) 
    as address_1, 
    cast (max(case when line = 2 then part_address end) as varchar2(40)) 
    as address_2, 
    cast (max(case when line = 3 then part_address end) as varchar2(40)) 
    as address_3 
from cte 
where part_address is not null 
group by street_address; 

Another SQL Fiddle.

Vale la pena notare che se la lunghezza dello street_address si avvicina a 120 caratteri, potrebbe non adattarsi perfettamente a 3 blocchi di 40 caratteri: si perdono alcuni caratteri a seconda della lunghezza della parola racchiusa nella "linea" successiva. . Questo approccio genererà più di 3 linee, ma la vista utilizza solo i primi tre, quindi potresti perdere la fine dell'indirizzo. Potresti voler rendere i campi più lunghi o avere un address_4 per quelle situazioni ...

Problemi correlati