2011-01-13 57 views
104

是否可以構建SQL以連接來自多個行的列值 ?用於連接來自Oracle中多行的列值的SQL查詢

下面是一個例子:

表的SQL的

 
PID 
A 
B 
C 

表B

 
PID SEQ Desc 

A  1  Have 
A  2  a nice 
A  3  day. 
B  1  Nice Work. 
C  1  Yes 
C  2  we can 
C  3  do 
C  4  this work! 

輸出應該是 -

 
PID Desc 
A  Have a nice day. 
B  Nice Work. 
C  Yes we can do this work! 

所以基本上說明列出put table是來自表B的SEQ值的串聯?

SQL的任何幫助?

+0

見例如:http://halisway.blogspot.com/2006/08/oracle-groupconcat-updated-again.html – Andomar 2011-01-13 23:40:35

+0

請看[該解決方案] (https://stackoverflow.com/a/19348687/2459039)。這對你有用。 – 2017-07-06 10:59:06

回答

156

根據您的版本有幾種方法 - 請參閱oracle documentation on string aggregation techniques。一個非常常見的是使用LISTAGG

SELECT pid, LISTAGG(Desc, ' ') WITHIN GROUP (ORDER BY seq) AS description 
FROM B GROUP BY pid; 

然後加入到A挑出pids你想要的。

注:開箱,LISTAGG只有VARCHAR2列正常工作。

+2

使用wm_concat()for Oracle 10g按照由逗號分隔的序號的升序連接文本,我們可以用其他東西進行降序分隔嗎? – jagamot 2011-03-07 21:01:57

-3

或Oracle STRAGG(列)函數。

我不得不說,這種處理是非常有限的......如果你超出字段寬度或顯示寬度的...

10

隨着SQL示範條款:

SQL> select pid 
    2  , ltrim(sentence) sentence 
    3 from (select pid 
    4    , seq 
    5    , sentence 
    6    from b 
    7   model 
    8     partition by (pid) 
    9     dimension by (seq) 
10     measures (descr,cast(null as varchar2(100)) as sentence) 
11     (sentence[any] order by seq desc 
12     = descr[cv()] || ' ' || sentence[cv()+1] 
13     ) 
14  ) 
15 where seq = 1 
16/

P SENTENCE 
- --------------------------------------------------------------------------- 
A Have a nice day 
B Nice Work. 
C Yes we can do this work! 

3 rows selected. 

我寫的這here。如果你按照OTN線程的鏈接,你會發現更多,包括性能比較。

14

還有一個XMLAGG功能,之前11.2適用於版本。因爲WM_CONCATundocumented and unsupported by Oracle,建議不要在生產系統中使用它。

隨着XMLAGG你可以做到以下幾點:

SELECT XMLAGG(XMLELEMENT(E,ename||',')).EXTRACT('//text()') "Result" 
FROM employee_names 

這樣做是

  • ename柱(用逗號連接後)的值從employee_names表中的XML元素(帶標記E)
  • 提取此文本
  • 彙總xml(連接它)
  • 調用結果列 「結果」
2

在你運行一個選擇查詢,運行此:

SET SERVEROUT ON SIZE 6000

SELECT XMLAGG(XMLELEMENT(E,SUPLR_SUPLR_ID||',')).EXTRACT('//text()') "SUPPLIER" 
FROM SUPPLIERS; 
-1

試試這個代碼:

SELECT XMLAGG(XMLELEMENT(E,fieldname||',')).EXTRACT('//text()') "FieldNames" 
    FROM FIELD_MASTER 
    WHERE FIELD_ID > 10 AND FIELD_AREA != 'NEBRASKA'; 
-2

在選擇要連接的位置時,調用SQL函數。

例如:

select PID, dbo.MyConcat(PID) 
    from TableA; 

那麼對於SQL函數:

Function MyConcat(@PID varchar(10)) 
returns varchar(1000) 
as 
begin 

declare @x varchar(1000); 

select @x = isnull(@x +',', @x, @x +',') + Desc 
    from TableB 
    where PID = @PID; 

return @x; 

end 

函數頭語法可能是錯的,但原則不會工作。

1

對於那些必須使用Oracle 9i(或更早版本)解決此問題的人員,您可能需要使用SYS_CONNECT_BY_PATH,因爲LISTAGG不可用。

要接聽OP,以下查詢將顯示來自表A PID和串聯從表B中的所有DESC列:

SELECT pid, SUBSTR (MAX (SYS_CONNECT_BY_PATH (description, ', ')), 3) all_descriptions 
FROM (
     SELECT ROW_NUMBER() OVER (PARTITION BY pid ORDER BY pid, seq) rnum, pid, description 
     FROM (
       SELECT a.pid, seq, description 
       FROM table_a a, table_b b 
       WHERE a.pid = b.pid(+) 
      ) 
    ) 
START WITH rnum = 1 
CONNECT BY PRIOR rnum = rnum - 1 AND PRIOR pid = pid 
GROUP BY pid 
ORDER BY pid; 

還可以有其中鍵和值都包含在一個實例表。可以在沒有表A並且只有表B存在的情況下使用以下查詢:

SELECT pid, SUBSTR (MAX (SYS_CONNECT_BY_PATH (description, ', ')), 3) all_descriptions 
FROM (
     SELECT ROW_NUMBER() OVER (PARTITION BY pid ORDER BY pid, seq) rnum, pid, description 
     FROM (
       SELECT pid, seq, description 
       FROM table_b 
      ) 
    ) 
START WITH rnum = 1 
CONNECT BY PRIOR rnum = rnum - 1 AND PRIOR pid = pid 
GROUP BY pid 
ORDER BY pid; 

所有值都可以根據需要重新排序。單獨的連接描述可以在PARTITION BY子句中重新排序,並且PID的列表可以在最後的ORDER BY子句中重新排序。


或者:有時可能當你想從整個表中的所有值連接成一排。

這裏的關鍵思想是對描述組使用人爲值進行連接。

在下面的查詢,常數串「1」被使用,但任何值將工作:

SELECT SUBSTR (MAX (SYS_CONNECT_BY_PATH (description, ', ')), 3) all_descriptions 
FROM (
     SELECT ROW_NUMBER() OVER (PARTITION BY unique_id ORDER BY pid, seq) rnum, description 
     FROM (
       SELECT '1' unique_id, b.pid, b.seq, b.description 
       FROM table_b b 
      ) 
    ) 
START WITH rnum = 1 
CONNECT BY PRIOR rnum = rnum - 1; 

單個級聯描述可以在PARTITION BY子句重新排序。

此頁面上的其他幾個答案也提到這個非常有用的參考: https://oracle-base.com/articles/misc/string-aggregation-techniques

0

我使用LISTAGG但返回此字符串波斯串!

我的查詢:

SELECT 
listagg(DESCRIPTION,' , ') within group (order by DESCRIPTION) 
FROM 
B_CEREMONY 

結果:

'A7'1 , ,4F 

請幫助我。

哇該解決方案的工作:

SELECT listagg(convert(DESCRIPTION, 'UTF8', 'AL16UTF16'),' , ') within group 
(order by DESCRIPTION) 
FROM B_CEREMONY; 
0

由於大部分的答案表明,LISTAGG是明顯的選擇。然而,隨着LISTAGG一個惱人的方面是,如果級聯字符串的總長度超過4000個字符(限制在SQL VARCHAR2),下面的錯誤被拋出,這是難以在Oracle版本管理高達12.1

ORA -01489:字符串連接的結果太長

12cR2中添加的新功能是LISTAGGON OVERFLOW子句。 包括本條款的查詢將如下所示:

SELECT pid, LISTAGG(Desc, ' ' on overflow truncate) WITHIN GROUP (ORDER BY seq) AS desc 
FROM B GROUP BY pid; 

以上可將輸出限制爲4000個字符,但不會拋出ORA-01489錯誤。

這些都是一些的ON OVERFLOW子句附加選項:

  • ON OVERFLOW TRUNCATE 'Contd..':這將在 顯示'Contd..'字符串的末尾(默認爲...
  • ON OVERFLOW TRUNCATE '':這將顯示4000個字符 沒有任何終止字符串。
  • ON OVERFLOW TRUNCATE WITH COUNT:這將在終止字符後面顯示總數爲 的字符數。 例如: - '...(5512)'
  • ON OVERFLOW ERROR:如果您預計LISTAGG失敗,並 ORA-01489錯誤(這是默認反正)。
0

11g和更高:使用listagg

SELECT 
    col1, 
    LISTAGG(col2, ', ') WITHIN GROUP (ORDER BY col2) "names" 
FROM table_x 
GROUP BY col1 

10g和下部:一種方法是使用一個函數:

CREATE OR REPLACE FUNCTION get_comma_separated_value (input_val in number) 
    RETURN VARCHAR2 
IS 
    return_text VARCHAR2(10000) := NULL; 
BEGIN 
    FOR x IN (SELECT col2 FROM table_name WHERE col1 = input_val) LOOP 
    return_text := return_text || ',' || x.col2 ; 
    END LOOP; 
    RETURN LTRIM(return_text, ','); 
END; 
/

爲了使用功能:

select col1, get_comma_separated_value(col1) from table_name 

注意:在某些較早版本的Oracle上有一個(不受支持的)函數WM_CONCAT,它可能會幫助您查看here for details

在MySQL:

SELECT col1, GROUP_CONCAT(col2) FROM table_name GROUP BY col1