2012-07-12 5 views
9

Ich möchte eine Verkettung von ein paar Feldern, aber mit einem Trennzeichen zwischen ihnen auswählen. Das Trennzeichen sollte nur vorhanden sein, wenn beide Operanden nicht null sind.Oracle: Concat mit Delimiter, aber nur, wenn beide Operanden NICHT NULL sind

Also für einen Datensatz mit a='foo', b=NULL, c='bar', möchte ich das Ergebnis abc='foo;bar' (nicht 'foo;;bar') bekommen.

Ich möchte eine Funktion wie concat_sep(a, b, ';') haben, die nur das ';' dazwischen, wenn sowohl a als auch b nicht null sind.

Natürlich kann ich NVL2 wie folgt verwenden:

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) 

Aber wie Sie sehen können, dieser Code wird bald cloggy, vor allem, wenn Sie mehr als 3 Spalten bekam, und Sie haben sie vernünftige Namen statt gegeben von a, b und c. ;-)

Ich konnte keinen kürzeren, einfacheren oder lesbareren Weg finden, aber ich dachte, ich würde hier fragen, bevor ich ganz aufhöre (oder Zeit verschwenden, eine solche Funktion selbst zu schreiben).

+0

wie sehr spezifische Logik y scheint ou wollen: warum wäre es eine Zeitverschwendung, selbst zu schreiben? – tbone

+0

Es wäre, wenn es sich bereits herausstellte. :) – GolezTrol

+0

ohne 11g listagg sieht aus wie Sie müssen Ihre eigenen schreiben. Und wenn Sie Ihre Kommentare betrachten, scheint es, dass Sie Ihre eigenen geschrieben haben, also bin ich verwirrt, suchen Sie nach einer Funktionalität, die Ihre eigene Funktion nicht bietet? Vielleicht ein Anwendungsfall Beispiel, um zu sehen, wie Sie dies verwenden möchten (ich denke an ein paar Ansätze) – tbone

Antwort

6

Ich weiß, dass Sie 10g verwenden, so dass es nicht funktioniert. Aber der Vollständigkeit halber behandelt LISTAGG()NULL Werte "richtig". Dafür müssten Sie 11g2 aktualisieren, aber:

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

Oder etwas prägnanter, wenn Sie Spalten aus einer Tabelle auflisten möchten:

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

oder gegen eine aktuelle Tabelle:

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; 

Jetzt bin ich nicht sicher, ob dies so viel besser ist (besser lesbar) als Ihr ursprüngliches Beispiel :-)

+0

Es fühlt sich ein bisschen eklig an, Spalten in eine Tabelle zu transformieren und diese dann zu aggregieren. Ich wollte es versuchen, zumindest als Lernbeispiel. :) Zu schlecht ORA_MINING_VARCHAR2_NT scheint auch nicht in 10g verfügbar zu sein, was schade ist, denn ich habe bereits einen LISTAGG-Ersatz für 10g geschrieben: http://stackoverflow.com/a/7885793/511529 – GolezTrol

+0

Ja, icky ist eben der Anfang. Früher hieß es 'DMSYS.ORA_MINING_VARCHAR2_NT'. [Diese Antwort] (http: // stackoverflow.com/a/8786893/521799) zeigt, wie Sie eine andere "SYS" -Tabelle/Varray-Typ finden, die Ihren Bedürfnissen entsprechen könnte –

+2

Auch nicht funktioniert. Anscheinend habe ich noch nicht die Data Mining Funktionen in meiner Datenbank. Ich werde es im Hinterkopf behalten, aber ich hatte das Gefühl, dass ich diese Lösung im Produktionscode sowieso nicht verwenden sollte. ;-) – GolezTrol

1

AFAIK, es gibt keine prägnante Möglichkeit, dies zu tun.

In der Vergangenheit habe ich zu

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

zurückgegriffen, aber das ist nicht besser als Ihr Beispiel.

+0

In der Tat, das ist ziemlich das Gleiche. Danke trotzdem. – GolezTrol