2012-07-12 10 views
9

Desidero selezionare una concatenazione di un paio di campi, ma con un separatore tra di essi. Il separatore dovrebbe essere presente solo se entrambi gli operandi non sono nulli.Oracle: Concat con delimitatore, ma solo se entrambi gli operandi NON sono NULL

Quindi per un record con a='foo', b=NULL, c='bar', voglio ottenere il risultato abc='foo;bar' (non 'foo;;bar').

Mi piacerebbe avere una funzione come concat_sep(a, b, ';') che aggiunge solo ';' inbetween se sia a che b non sono nulli.

Naturalmente, posso usare NVL2 come questo:

select 
    a, b, c, 
    substr(abc, 1, length(abc) - 1) as abc 
from 
    (select 
    a, b, c, 
    nvl2(a, a || ';', '') || nvl2(b, b || ';', '') || nvl2(c, c || ';', '') as abc 
    from 
    Table1) 

Ma, come si può vedere, il codice diventa Cloggy presto, soprattutto quando si ha più di 3 colonne e hai dato loro dei nomi sensati, invece di a, b e c. ;-)

Non sono riuscito a trovare un modo più breve, più facile o più leggibile, ma ho pensato di chiederlo prima di rinunciare del tutto (o perdere tempo a scrivere una tale funzione da solo).

+0

sembra come molto specifica logica y vuoi: perché scrivere da solo la tua stessa funzione è una perdita di tempo? – tbone

+0

Sarebbe se ce ne fosse già uno. :) – GolezTrol

+0

senza 11g listagg sembra che avrai bisogno di scrivere il tuo. E guardando i tuoi commenti, sembra che tu abbia scritto il tuo, quindi sono confuso, stai cercando alcune funzionalità che la tua funzione non fornisce? Forse un esempio di caso d'uso per vedere come prevedi di usarlo (posso pensare ad alcuni approcci) – tbone

risposta

6

So che stai usando 10g, quindi non funzionerà. Ma per completezza, LISTAGG() gestisce i valori NULL "correttamente". Per questo sarebbe necessario aggiornare a 11g2, però:

-- Some sample data, roughly equivalent to yours 
with t as (
    select 'foo' as x from dual union all 
    select null  from dual union all 
    select 'bar'  from dual 
) 
-- Use the listagg aggregate function to join all values 
select listagg(x, ';') within group (order by rownum) 
from t; 

o un po 'più succinta, se si desidera elencare le colonne da una tabella:

-- I use SYS.ORA_MINING_VARCHAR2_NT as a TABLE TYPE. Use your own, if you prefer 
select listagg(column_value, ';') within group (order by rownum) 
from table(ORA_MINING_VARCHAR2_NT('foo', null, 'bar')); 

O contro una tabella effettiva:

select listagg(column_value, ';') 
     within group (order by rownum) 
from Table1 
cross join table(ORA_MINING_VARCHAR2_NT(Table1.a, Table1.b, Table1.c)) 
group by Table1.id; 

Ora io non sono sicuro se questo è molto meglio (più leggibile) di quanto il tuo esempio originale :-)

+0

Sembra un po 'difficile convertire le colonne in una tabella e quindi aggregarle. Volevo provarlo, almeno come esempio di apprendimento. :) Peccato che anche ORA_MINING_VARCHAR2_NT non sia disponibile in 10g, il che è un peccato perché, ho già scritto una sostituzione di LISTAGG per 10g: http://stackoverflow.com/a/7885793/511529 – GolezTrol

+0

Sì, icky è solo l'inizio. Si chiamava 'DMSYS.ORA_MINING_VARCHAR2_NT'. [Questa risposta] (http: // StackOverflow.it/a/8786893/521799) mostra come puoi trovare un'altra tabella 'SYS'/tipo varray che possa soddisfare le tue esigenze –

+2

Inoltre non funziona. Apparentemente non ho ancora le funzionalità di data mining nel mio database. Lo terrò a mente, ma ho la sensazione che non dovrei usare comunque questa soluzione nel codice di produzione. ;-) – GolezTrol

1

AFAIK, non esiste un modo succinto per farlo.

In passato, ho fatto ricorso a

SELECT a 
||  DECODE(b 
     ,  NULL, NULL 
     ,  ';' || b) 
||  DECODE(c 
     ,  NULL, NULL 
     ,  ';' || c) 
||  DECODE(d 
     ,  NULL, NULL 
     ,  ';' || d) 
... 
FROM table1 

ma che c'è di meglio che il vostro esempio.

+0

In effetti, è praticamente la stessa cosa. Grazie comunque. – GolezTrol

Problemi correlati