我為你狂
          為你而瘋狂
          posts - 29,comments - 9,trackbacks - 0
          怎么合并多行記錄的字符串,一直是oracle新手喜歡問的SQL問題之一,關(guān)于這個問題的帖子我看過不下30個了,現(xiàn)在就對這個問題,進(jìn)行一個總結(jié)。
             什么是合并多行字符串(連接字符串)呢,例如:
          SQL> desc test;
          Name    Type         Nullable Default Comments 
          ------- ------------ -------- ------- -------- 
          COUNTRY VARCHAR2(20) Y                         
          CITY    VARCHAR2(20) Y                         

          SQL> select * from test;

          COUNTRY              CITY
          -------------------- --------------------
          中國                 臺北
          中國                 香港
          中國                 上海
          日本                 東京
          日本                 大阪
          要求得到如下結(jié)果集:
          -------    --------------------
          中國       臺北,香港,上海
          日本       東京,大阪
            實(shí)際就是對字符實(shí)現(xiàn)一個聚合功能,我很奇怪為什么Oracle沒有提供官方的聚合函數(shù)來實(shí)現(xiàn)它呢:)
            下面就對幾種經(jīng)常提及的解決方案進(jìn)行分析(有一個評測標(biāo)準(zhǔn)最高★★★★★):
          1.被集合字段范圍小且固定型 靈活性★   性能★★★★ 難度 ★
            這種方法的原理在于你已經(jīng)知道CITY字段的值有幾種,且還不算太多,如果太多這個SQL就會相當(dāng)?shù)?br style="font-family: " />
          長。。看例子:
          SQL> select t.country,
            2  MAX(decode(t.city,'臺北',t.city||',',NULL)) ||
            3  MAX(decode(t.city,'香港',t.city||',',NULL))||
            4  MAX(decode(t.city,'上海',t.city||',',NULL))||
            5  MAX(decode(t.city,'東京',t.city||',',NULL))||
            6  MAX(decode(t.city,'大阪',t.city||',',NULL))
            7  from test t GROUP BY t.country
            8  /

          COUNTRY              MAX(DECODE(T.CITY,'臺北',T.CIT
          -------------------- ------------------------------
          中國                 臺北,香港,上海,
          日本                 東京,大阪,
            大家一看,估計(jì)就明白了(如果不明白,好好補(bǔ)習(xí)MAX DECODE和分組)。這種方法無愧為最笨的方法

          ,但是對某些應(yīng)用來說,最有效的方法也許就是它。
          2.固定表固定字段函數(shù)法 靈活性★★ 性能★★★★ 難度 ★★
            此法必須預(yù)先知道是哪個表,也就是說一個表就得寫一個函數(shù),不過方法1的一個取值就要便捷多了。在大多數(shù)應(yīng)用中,也不會存在大量這種合并字符串的需求。廢話完畢,看下面:
            定義一個函數(shù)
          create or replace function str_list( str_in in varchar2 )--分類字段
            return varchar2
          is
                str_list  varchar2(4000) default null;--連接后字符串
                str  varchar2(20) default null;--連接符號
          begin
                for x in ( select TEST.CITY from TEST where TEST.COUNTRY = str_in ) loop
                    str_list := str_list || str || to_char(x.city);
                    str := ', ';
                end loop;
                return str_list;
          end;
          使用:
          SQL> select DISTINCT(T.country),list_func1(t.country) from test t;

          COUNTRY              LIST_FUNC1(T.COUNTRY)
          -------------------- ----------------
          中國                 臺北, 香港, 上海
          日本                 東京, 大阪

          SQL> select t.country,str_list(t.country) from test t GROUP BY t.country;

          COUNTRY              STR_LIST(T.COUNTRY)
          -------------------- -----------------------
          中國                 臺北, 香港, 上海
          日本                 東京, 大阪
          這個時候,使用分組和求唯一都可以滿足要求。它的原理就是,根據(jù)唯一的分組字段country,在函數(shù)里面再次查詢該字段對應(yīng)的所有被合并列,使用PL/SQL將其合并輸出。
          3.靈活表函數(shù)法 靈活性★★★ 性能★★★ 難度 ★★★
            該方法是在方法2的基礎(chǔ)上,使用動態(tài)SQL,將表名和字段名稱傳入,從而達(dá)到靈活的目的。
            create or replace function str_list2( key_name in varchar2,
                              key  in varchar2,
                              coname in varchar2,
                              tname     in varchar2 )
           return varchar2
          as
              type rc is ref cursor;
              str    varchar2(4000);
              sep    varchar2(2);
              val    varchar2(4000);
              cur    rc;
          begin
              open cur for 'select '||coname||'
                              from '|| tname || '
                              where ' || key_name || ' = :x '
                          using key;
              loop
                  fetch cur into val;
                  exit when cur%notfound;
                  str := str || sep || val;
                  sep := ', ';
              end loop;
              close cur;
              return str;
          end;
          SQL> select test.country,
            2  str_list2('COUNTRY', test.country, 'CITY', 'TEST') emplist
            3  from test
            4   group by test.country
            5  /

          COUNTRY              EMPLIST
          -------------------- -----------------
          中國                 臺北, 香港, 上海
          日本                 東京, 大阪
          4.一條SQL法 靈活性★★★★ 性能★★ 難度 ★★★★
            一條SQL的法則是某位大師提出的,大家曾經(jīng)在某個時期都樂此不彼的尋求各種的問題一條SQL法,但是大師的意思似乎被曲解,很多性能差,可讀性差,靈活差的SQL都是這個原則產(chǎn)物,所謂畫虎不成反成犬類。不過,解決問題始終是第一原則,這里還是給出一個比較有代表性的一條SQL方法。
          SELECT country,max(substr(city,2)) city
            FROM
          (SELECT country,sys_connect_by_path(city,',') city
           FROM 
          (SELECT country,city,country||rn rchild,country||(rn-1) rfather
            FROM 
            (SELECT  test.country ,test.city,row_number() over (PARTITION BY test.country ORDER BY 

          test.city) rn 
            FROM test))
          CONNECT BY PRIOR rchild=rfather START WITH rfather LIKE '%0')
           GROUP BY country;
            下面分步解析,有4個FROM,就有4次結(jié)果集的操作。
             step 1 給記錄加上序號rn
            SQL> SELECT  test.country ,test.city,row_number() over (PARTITION BY test.country ORDER 

          BY test.city) rn
            2    FROM test
            3  /

          COUNTRY              CITY                         RN
          -------------------- -------------------- ----------
          日本                 大阪                          1
          日本                 東京                          2
          中國                 上海                          1
          中國                 臺北                          2
          中國                 香港                          3
            step 2 創(chuàng)造子節(jié)點(diǎn)父節(jié)點(diǎn)
          SQL> SELECT country,city,country||rn rchild,country||(rn-1) rfather
            2    FROM
            3    (SELECT  test.country ,test.city,row_number() over (PARTITION BY test.country ORDER 

          BY test.city) rn
            4    FROM test)
            5  /
           日本 大阪 日本1 日本0
           日本 東京 日本2 日本1
           中國 上海 中國1 中國0
           中國 臺北 中國2 中國1
           中國 香港 中國3 中國2
            step 3 利用sys_connect_by_path生成結(jié)果集
          SELECT country,sys_connect_by_path(city,',') city
           FROM 
          (SELECT country,city,country||rn rchild,country||(rn-1) rfather
            FROM 
            (SELECT  test.country ,test.city,row_number() over (PARTITION BY test.country ORDER BY 

          test.city) rn 
            FROM test))
          CONNECT BY PRIOR rchild=rfather START WITH rfather LIKE '%0'
           日本 ,大阪
           日本 ,大阪,東京
           中國 ,上海
           中國 ,上海,臺北
           中國 ,上海,臺北,香港
            step 4 最終步驟,篩選結(jié)果集合
          SQL> SELECT country,max(substr(city,2)) city
            2    FROM
            3  (SELECT country,sys_connect_by_path(city,',') city
            4   FROM
            5  (SELECT country,city,country||rn rchild,country||(rn-1) rfather
            6    FROM
            7    (SELECT  test.country ,test.city,row_number() over (PARTITION BY test.country ORDER 

          BY test.city) rn
            8    FROM test))
            9  CONNECT BY PRIOR rchild=rfather START WITH rfather LIKE '%0')
           10   GROUP BY country;

          COUNTRY              CITY
          -------------------- -------
          中國                 上海,臺北,香港
          日本                 大阪,東京

            可謂是,7歪8搞,最后還是弄出來了,呵呵。
          5.自定義聚合函數(shù) 靈活性★★★★★ 性能★★★★★ 難度 ★★★★★
            最后一個方法是我認(rèn)為“王道”的方法,自定義聚合函數(shù)。
            就如何我在本開始說的,為啥oracle沒有這種聚合函數(shù)呢?我也不知道,但oracle提供了聚合函數(shù)的

          API可以讓我方便的自己定義聚合函數(shù)。
            詳細(xì)可以看Oracle Data Catridge guide這個文檔。連接如下:
            http://www.oracle.com.cn/other/9ionlinedoc/appdev.920/a96595/toc.htm
          下面給出一個簡單的例子:
          SQL> SELECT t.country,strcat(t.city) FROM test t GROUP BY t.country;

          COUNTRY              STRCAT(T.CITY)
          -------------------- ------------------
          日本                 東京,大阪
          中國                 臺北,香港,上海
          簡單吧,和官方的函數(shù)一樣的便捷高效。
          函數(shù):
          CREATE OR REPLACE FUNCTION strcat(input varchar2 )
          RETURN varchar2
          PARALLEL_ENABLE AGGREGATE USING strcat_type;
          TYPE:
          create or replace type strcat_type as object (
              cat_string varchar2(4000),
              static function ODCIAggregateInitialize(cs_ctx In Out strcat_type) return number,
              member function ODCIAggregateIterate(self In Out strcat_type,value in varchar2) return 

          number,
              member function ODCIAggregateMerge(self In Out strcat_type,ctx2 In Out strcat_type) 

          return number,
              member function ODCIAggregateTerminate(self In Out strcat_type,returnValue Out 

          varchar2,flags in number) return number
          )
          6.待發(fā)掘...
            總結(jié),合并字符串還有更多的方法希望大家能發(fā)掘,本文的目的主要是拋磚引玉,如果有新的發(fā)現(xiàn)我會繼續(xù)更新方法。需要注意的問題是,本文采用varchar2為例子,所以長度有限制,oracle的版本對方法的實(shí)現(xiàn)也影響。
          posted on 2009-06-25 19:33 小虎(年輕) 閱讀(326) 評論(0)  編輯  收藏 所屬分類: Oracle相關(guān)
          主站蜘蛛池模板: 西乌珠穆沁旗| 大名县| 防城港市| 阿荣旗| 泰州市| 平邑县| 凌源市| 河间市| 巴林右旗| 博兴县| 鱼台县| 佛山市| 阳朔县| 德州市| 广汉市| 乌拉特前旗| 阆中市| 逊克县| 科技| 江山市| 临颍县| 乌拉特中旗| 闽清县| 兰溪市| 高青县| 广丰县| 贵州省| 阿尔山市| 庄浪县| 西丰县| 涟水县| 页游| 柯坪县| 彭州市| 呈贡县| 吕梁市| 涪陵区| 南华县| 玉树县| 信丰县| 平塘县|