2012-07-12 12 views
9

Tôi muốn chọn một kết nối của một vài trường, nhưng với dấu tách giữa chúng. Dấu phân cách chỉ nên ở đó nếu cả hai toán hạng không phải là rỗng.Oracle: Concat với delimiter, nhưng chỉ khi cả hai toán hạng là NOT NULL

Vì vậy, để lưu bản ghi với a='foo', b=NULL, c='bar', tôi muốn nhận kết quả abc='foo;bar' (không phải 'foo;;bar').

Tôi muốn có chức năng như concat_sep(a, b, ';') chỉ thêm ';' inbetween nếu cả a và b không null.

Tất nhiên, tôi có thể sử dụng nvl2 như thế này:

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) 

Nhưng như bạn thấy, đoạn mã này trở thành sự bít lại sớm, đặc biệt là khi bạn có nhiều hơn 3 cột và bạn đã cho chúng những cái tên hợp lý thay vì của a, b và c. ;-)

Tôi không thể tìm thấy một cách ngắn hơn, dễ dàng hơn hoặc dễ đọc hơn, nhưng tôi nghĩ tôi sẽ hỏi ở đây trước khi từ bỏ hoàn toàn (hoặc lãng phí thời gian viết một hàm như vậy).

+0

dường như logic y rất cụ thể Bạn muốn: tại sao viết chính chức năng của chính bạn là một sự lãng phí thời gian? – tbone

+0

Nó sẽ là nếu có bật ra được một đã. :) – GolezTrol

+0

mà không có 11g listagg trông giống như bạn S need cần phải viết của riêng bạn. Và nhìn nhận xét của bạn, có vẻ như bạn DID viết của riêng bạn, vì vậy tôi bối rối, bạn đang tìm kiếm một số chức năng chức năng của riêng bạn không cung cấp? Có lẽ một ví dụ về trường hợp sử dụng để xem cách bạn dự định sử dụng điều này (tôi có thể nghĩ ra một vài cách tiếp cận) – tbone

Trả lời

6

Tôi biết bạn đang sử dụng 10g, vì vậy điều đó sẽ không hoạt động. Nhưng để hoàn thành, LISTAGG() xử lý các giá trị NULL "chính xác". Cho rằng bạn sẽ phải nâng cấp lên 11g2, mặc dù:

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

Hoặc một chút gọn gàng hơn, nếu bạn muốn liệt kê các cột từ một bảng:

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

Hoặc chống lại một bảng thực tế:

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; 

Bây giờ tôi không chắc chắn nếu điều này là tốt hơn rất nhiều (dễ đọc hơn) so với ví dụ ban đầu của bạn :-)

+0

Nó cảm thấy một chút icky để chuyển đổi cột vào một bảng và sau đó tổng hợp đó. Tôi muốn thử nó, ít nhất là một ví dụ học tập. :) Quá xấu ORA_MINING_VARCHAR2_NT cũng dường như không có sẵn trong 10g, đó là một điều đáng tiếc bởi vì, tôi đã viết một thay thế LISTAGG cho 10g: http://stackoverflow.com/a/7885793/511529 – GolezTrol

+0

Vâng, icky chỉ là sự bắt đầu. Nó thường được gọi là 'DMSYS.ORA_MINING_VARCHAR2_NT'. [Câu trả lời này] (http: // stackoverflow.com/a/8786893/521799) cho thấy cách bạn có thể tìm thấy một loại bảng/biến thể 'SYS' khác có thể phù hợp với nhu cầu của bạn –

+2

Cũng không hoạt động. Dường như tôi chưa có các tính năng khai phá dữ liệu trong cơ sở dữ liệu của tôi. Tôi sẽ giữ nó trong tâm trí, nhưng tôi có một cảm giác tôi không nên sử dụng giải pháp này trong mã sản xuất anyway. ;-) – GolezTrol

1

AFAIK, không có cách nào ngắn gọn để thực hiện việc này.

Trong quá khứ, tôi đã viện đến

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

nhưng điều đó không có gì tốt hơn so với ví dụ của bạn.

+0

Thật vậy, đó là khá nhiều như nhau. Cảm ơn không kém. – GolezTrol