如何对多条字符串记录进行"汇总"运算?
我有多条记录,要对其分类汇总.如下表,想通过形如 SELECT cardtype,sum(numbers) AS 数量, str_sum(CARDID) AS 号码范围 FROM card GROUP BY  cardtype.
原表记录:
cardtype numbers cardid
A 3 01001
A 2 01002
A 1 01003
A 2 01007
B 3 05005
B 5 05006
统计后形成:
cardtype 数量 号码范围
A 8 01001-01003,01007
B 8 05005-05006现在这个str_sum()函数,就是我想象出来的功能,不能哪位大侠可以帮我写出解答?
当然,如果能够求出 01001,01002,01003,01007这样的结果也算是帮了大忙了,我再想办法变成01001-01003,01007的形式.
(注:我是想用vs2008c#+sqlserver2005写一个简单的项目,如果能用oracle写的SQL也行)

解决方案 »

  1.   

    SQL> edit
    已写入 file afiedt.buf  1  create or replace function str_num(ctype in varchar2)
      2  return varchar2
      3  is
      4  rs varchar2(4000);
      5  begin
      6  for x in (select cardid from t where cardtype=ctype) loop
      7  rs:=rs||','||x.cardid;
      8  end loop;
      9  return rs;
     10* end;
     11  /函数已创建。SQL> select str_num(cardtype) from t group by cardtype;STR_NUM(CARDTYPE)
    -------------------------------------------------------------------------,01001,01002,01003,01007
    ,05006,05005SQL> select * from t;CA    NUMBERS CARDID
    -- ---------- ----------
    A           1 01001
    A           2 01002
    A           3 01003
    A           2 01007
    B           3 05006
    B           5 05005已选择6行。前面多了个逗号,你自己再处理一下吧
      

  2.   

    SQL> SELECT cardtype,sum(numbers) AS 数量, str_num(CARDtype) AS 号码范围 FROM t GROUP BY  cardtype;CA       数量 号码范围
    -- ---------- ----------------------------------------
    A           8 ,01001,01002,01003,01007
    B           8 ,05006,05005
      

  3.   


    DROP TABLE t;
    CREATE TABLE t(cardtype VARCHAR2(10),numbers INT,cardid VARCHAR2(10));
    INSERT INTO T VALUES ('A', 3, '01001');
    INSERT INTO T VALUES ('A', 2, '01002');
    INSERT INTO T VALUES ('A', 1, '01003');
    INSERT INTO T VALUES ('A', 2, '01007');
    INSERT INTO T VALUES ('B', 3, '05005');
    INSERT INTO T VALUES ('B', 5, '05006');CREATE OR REPLACE FUNCTION MY_CONCAT(P_cardtype  VARCHAR2) RETURN VARCHAR2 IS
      V_STR VARCHAR2(2000) := '';
      TYPE XX IS REF CURSOR;
      R          XX;
      V_TEMP_STR VARCHAR2(100);
    BEGIN
      OPEN R FOR 'SELECT cardid  FROM t WHERE cardtype=''' || P_cardtype||'''';
      LOOP
        FETCH R
          INTO V_TEMP_STR;
        EXIT WHEN R%NOTFOUND;
        IF LENGTH(V_STR) > 0 THEN
          V_STR := V_STR || ',' || V_TEMP_STR;
        ELSE
          V_STR :=  V_TEMP_STR;
        END IF;
      END LOOP;
      CLOSE R;
      RETURN(V_STR);
    END MY_CONCAT;
    /
    SELECT cardtype,MY_CONCAT(cardtype) CARDID FROM t GROUP BY cardtype;
    --或者:
    SELECT CARDTYPE,wmsys.wm_concat(CARDID)
      FROM t GROUP BY cardtype;
    /*
    输出:
    CARDTYPE  CARDTYPE 
    A 01001,01002,01003,01007
    B 05005,05006
    */SELECT CARDTYPE,wmsys.wm_concat(CARDID) CARDID
      FROM (SELECT CARDTYPE,
                   DECODE(TO_CHAR(MAX(CARDID)),
                          TO_CHAR(MIN(CARDID)),
                          TO_CHAR(MIN(CARDID)),
                          TO_CHAR(MIN(CARDID)) || '-' || TO_CHAR(MAX(CARDID))) CARDID
              FROM (SELECT CARDTYPE, CARDID, COL, COL - ROWNUM COL1
                      FROM (SELECT CARDTYPE, CARDID, N COL
                              FROM (SELECT CARDTYPE, CARDID, TO_NUMBER(CARDID) N
                                      FROM T)
                             ORDER BY CARDTYPE, N))
             GROUP BY CARDTYPE, COL1
             ORDER BY 1, 2)
     GROUP BY CARDTYPE;/*输出:
    CARDTYPE  CARDID 
    A 01001-01003,01007
    B 05005-05006
    */
      

  4.   

    TRY IT ..
    SQL> SELECT * FROM CARD;CARDTYPE      NUMBERS CARID
    ---------- ---------- ----------
    A                   3 01001
    A                   2 01002
    A                   1 01003
    A                   2 01007
    B                   3 05005
    B                   5 050066 rows selectedSQL> 
    SQL> SELECT DISTINCT CARDTYPE,
      2                  COUNTS,
      3                  LTRIM(FIRST_VALUE(SYS_CONNECT_BY_PATH(SEGMENTS, ','))
      4                        OVER(PARTITION BY CARDTYPE ORDER BY LEVEL DESC),
      5                        ',')
      6    FROM (SELECT CARDTYPE,
      7                 COUNTS,
      8                 SEGMENTS,
      9                 CARDTYPE || ROW_NUMBER() OVER(PARTITION BY CARDTYPE ORDER BY SEGMENTS) "FRONTS",
     10                 CARDTYPE ||
     11                 (ROW_NUMBER()
     12                  OVER(PARTITION BY CARDTYPE ORDER BY SEGMENTS) + 1) "BEHINDS"
     13            FROM (SELECT CARDTYPE,
     14                         MAX(COUNTS) "COUNTS",
     15                         DECODE(MIN(CARID),
     16                                MAX(CARID),
     17                                MIN(CARID),
     18                                MIN(CARID) || '-' || MAX(CARID)) "SEGMENTS"
     19                    FROM (SELECT CARDTYPE,
     20                                 CARID,
     21                                 COUNT(1) OVER(PARTITION BY CARDTYPE) "COUNTS",
     22                                 TO_NUMBER(CARID) -
     23                                 (ROW_NUMBER()
     24                                  OVER(PARTITION BY CARDTYPE ORDER BY CARID)) DIF
     25                            FROM CARD)
     26                   GROUP BY CARDTYPE, DIF) T2) T3
     27  CONNECT BY PRIOR BEHINDS = FRONTS;CARDTYPE       COUNTS LTRIM(FIRST_VALUE(SYS_CONNECT_
    ---------- ---------- --------------------------------------------------------------------------------
    A                   4 01001-01003,01007
    B                   2 05005-05006SQL> 
      

  5.   

    增加对numbers的sum后:DROP TABLE t;
    CREATE TABLE t(cardtype VARCHAR2(10),numbers INT,cardid VARCHAR2(10));
    INSERT INTO T VALUES ('A', 3, '01001');
    INSERT INTO T VALUES ('A', 2, '01002');
    INSERT INTO T VALUES ('A', 1, '01003');
    INSERT INTO T VALUES ('A', 2, '01007');
    INSERT INTO T VALUES ('B', 3, '05005');
    INSERT INTO T VALUES ('B', 5, '05006');CREATE OR REPLACE FUNCTION MY_CONCAT(P_cardtype  VARCHAR2) RETURN VARCHAR2 IS
      V_STR VARCHAR2(2000) := '';
      TYPE XX IS REF CURSOR;
      R          XX;
      V_TEMP_STR VARCHAR2(100);
    BEGIN
      OPEN R FOR 'SELECT cardid  FROM t WHERE cardtype=''' || P_cardtype||'''';
      LOOP
        FETCH R
          INTO V_TEMP_STR;
        EXIT WHEN R%NOTFOUND;
        IF LENGTH(V_STR) > 0 THEN
          V_STR := V_STR || ',' || V_TEMP_STR;
        ELSE
          V_STR :=  V_TEMP_STR;
        END IF;
      END LOOP;
      CLOSE R;
      RETURN(V_STR);
    END MY_CONCAT;
    /
     
    SELECT cardtype,sum(numbers) sn,MY_CONCAT(cardtype) CARDID FROM t GROUP BY cardtype;
    --或者(10g):
    SELECT CARDTYPE,sum(numbers) sn,wmsys.wm_concat(CARDID) CARDID
      FROM t GROUP BY cardtype;
    /*
    输出:
    CARDTYPE SN CARDID
    A 8 01001,01002,01003,01007
    B 8 05005,05006
    */--一下是10g方法:
    SELECT CARDTYPE, SUM(SN), WMSYS.WM_CONCAT(CARDID) CARDID
      FROM (SELECT CARDTYPE,
                   SUM(NUMBERS) SN,
                   DECODE(TO_CHAR(MAX(CARDID)),
                          TO_CHAR(MIN(CARDID)),
                          TO_CHAR(MIN(CARDID)),
                          TO_CHAR(MIN(CARDID)) || '-' || TO_CHAR(MAX(CARDID))) CARDID
              FROM (SELECT CARDTYPE, NUMBERS, CARDID, COL, COL - ROWNUM COL1
                      FROM (SELECT CARDTYPE, NUMBERS, CARDID, N COL
                              FROM (SELECT CARDTYPE,
                                           NUMBERS,
                                           CARDID,
                                           TO_NUMBER(CARDID) N
                                      FROM T)
                             ORDER BY CARDTYPE, N))
             GROUP BY CARDTYPE, COL1
             ORDER BY 1, 3)
     GROUP BY CARDTYPE;/*输出:
    CARDTYPE SUM(SN) CARDID
    A   8        01001-01003,01007
    B   8        05005-05006
    */
      

  6.   

    如果是求SUM(numbers) 而不是求COUNT,把 COUNT(1) OVER(PARTITION BY CARDTYPE)换成SUM(NUMBERS) OVER(PARTITION BY CARDTYPE)就可以了。
      

  7.   

    也可以考虑用FUNCTION实现:
    /*
         Author: mantisXF
         Date: Sep 7, 2008
         
         GROUPID      -- Your group data vlaue   E.G: 'A' OR COLUMN_NAME: CARDTYPE
         GROUPNAME    -- Your group column name
         COLUMN_NAME  -- Your column name which you need to return
         SCHEMA_NAME  -- Your schema name of table
         TABLE_NAME   -- Your table name*/CREATE OR REPLACE FUNCTION SCOTT.GET_CONTINUUM_NUMBERS(GROUPID IN VARCHAR2,
                                                     GROUPNAME IN VARCHAR2,
                                                     COLUMN_NAME IN VARCHAR2,
                                                     SCHEMA_NAME IN VARCHAR2, 
                                                     TABLE_NAME IN VARCHAR2) RETURN VARCHAR2 IS
      -- DECLARE VARIABLE
      CUR_NUMBERS SYS_REFCURSOR;
      V_CON_NUMBERS VARCHAR2(1000) := '';
      V_MIN_NUMBERS VARCHAR2(30) := '';
      V_MAX_NUMBERS VARCHAR2(30) := '';
      V_TEMP_NUMBERS VARCHAR2(30) := '';
      V_SQL VARCHAR2(100);  V_STEP VARCHAR2(100) := '';
      
    BEGIN
      V_STEP := 'GET THE CURSOR BY PARTICULARLY GROUP_ID';
      V_SQL := 'SELECT '||COLUMN_NAME||' FROM '||SCHEMA_NAME||'.'||TABLE_NAME||' WHERE '||GROUPNAME||' = :X ORDER BY '||COLUMN_NAME||' ';
      DBMS_OUTPUT.PUT_LINE('V_SQL: '||V_SQL);
      OPEN CUR_NUMBERS FOR V_SQL USING GROUPID;
      
      V_STEP := 'LOOP THE RESULT';
      LOOP
      FETCH CUR_NUMBERS INTO V_TEMP_NUMBERS;
      
        V_STEP := 'DECIDE WHETHER V_TEMP_NUMBERS HAVING VALUE';
        IF LENGTH(V_TEMP_NUMBERS) > 0 THEN
          V_STEP := 'DECIDE WHETHER V_MIN_NUMBE HAVING VALUE';
          IF V_MAX_NUMBERS IS NOT NULL THEN
            V_STEP := 'DECIDE WHETHER IT HAVING CONTINUUM_NUMBERS VALUE';
            IF TO_NUMBER(V_TEMP_NUMBERS) - NVL(TO_NUMBER(V_MAX_NUMBERS),0) = 1 THEN
              V_MAX_NUMBERS := V_TEMP_NUMBERS;
            ELSE
              V_STEP := 'DECIDE WHETHER V_MIN_NUMBE = V_MAX_NUMB. IT MEANS JUST HAVING 1 VALUE';
              IF V_MIN_NUMBERS = V_MAX_NUMBERS THEN
                V_CON_NUMBERS := V_CON_NUMBERS||','||V_MIN_NUMBERS;
              ELSE 
                V_CON_NUMBERS := V_CON_NUMBERS||','||V_MIN_NUMBERS||'-'||V_MAX_NUMBERS;
                V_MIN_NUMBERS := V_TEMP_NUMBERS;
                V_MAX_NUMBERS := V_TEMP_NUMBERS;
              END IF;
            END IF;
          ELSE
            V_MIN_NUMBERS := V_TEMP_NUMBERS;
            V_MAX_NUMBERS := V_TEMP_NUMBERS;
          END IF;
        ELSE 
          V_CON_NUMBERS := V_CON_NUMBERS||V_TEMP_NUMBERS;
        END IF;
      
        EXIT WHEN CUR_NUMBERS%NOTFOUND;  END LOOP;
      
      V_STEP := 'CLOSE CURSOR CUR_NUMBERS';
      IF CUR_NUMBERS%ISOPEN THEN
        CLOSE CUR_NUMBERS;
      END IF;
      
      V_STEP := 'RETURN V_CON_NUMBERS';
      RETURN(LTRIM(V_CON_NUMBERS,','));EXCEPTION
       WHEN OTHERS THEN
       DBMS_OUTPUT.PUT_LINE('Error at: '||V_STEP);
       DBMS_OUTPUT.PUT_LINE(SQLCODE||' : '||SQLERRM);
      
    END GET_CONTINUUM_NUMBERS;
    -- 实现方式SQL> SELECT * FROM SCOTT.CARD;CARDTYPE      NUMBERS CARDID
    ---------- ---------- ----------
    A                   3 01001
    A                   2 01002
    A                   1 01003
    A                   2 01007
    B                   3 05005
    B                   5 050066 rows selectedSQL> SELECT CARDTYPE,
      2         SUM(NUMBERS),
      3         SCOTT.GET_CONTINUUM_NUMBERS(CARDTYPE,'CARDTYPE','CARDID','SCOTT','CARD') "STR_SUM"
      4    FROM SCOTT.CARD
      5   GROUP BY CARDTYPE;CARDTYPE   SUM(NUMBERS) STR_SUM
    ---------- ------------ --------------------------------------------------------------------------------
    A                     8 01001-01003,01007
    B                     8 05005-05006SQL> 
      

  8.   

    感谢 wffffc\mantisXF \oracledbalgtu 三位高手,给了我详尽的解答,十分佩服!
      

  9.   

    菜鸟问oracledbalgtu兄:
    SELECT CARDTYPE,wmsys.wm_concat(CARDID) CARDIDwmsys.wm_concat是什么函数?不明白啊
     而且
      
       《-------  SELECT CARDTYPE,wmsys.wm_concat(CARDID) CARDID
      FROM (SELECT CARDTYPE,
                   DECODE(TO_CHAR(MAX(CARDID)),
                          TO_CHAR(MIN(CARDID)),
                          TO_CHAR(MIN(CARDID)),
                          TO_CHAR(MIN(CARDID)) || '-' || TO_CHAR(MAX(CARDID))) CARDID
              FROM (SELECT CARDTYPE, CARDID, COL, COL - ROWNUM COL1
                      FROM (SELECT CARDTYPE, CARDID, N COL
                              FROM (SELECT CARDTYPE, CARDID, TO_NUMBER(CARDID) N
                                      FROM T)
                             ORDER BY CARDTYPE, N))
             GROUP BY CARDTYPE, COL1
             ORDER BY 1, 2)
     GROUP BY CARDTYPE;  》
       查出来的值还是   A          8 ,01001,01002,01003,01007 
       B          8 ,05006,05005   的形式啊
      请指点!!!
      

  10.   

    wmsys.wm_concat函数是把以CARDTYPE分组后的同一列的几行数据变成一行数据,并以逗号的方式分隔. 用下面的SQL试试。
    SQL> SELECT CARDTYPE, COUNTS, WMSYS.WM_CONCAT(SEGMENTS) "STR_SUM"
      2    FROM (SELECT CARDTYPE,
      3                 COUNTS,
      4                 DECODE(MIN(CARDID),
      5                        MAX(CARDID),
      6                        MIN(CARDID),
      7                        MIN(CARDID) || '-' || MAX(CARDID)) "SEGMENTS"
      8            FROM (SELECT CARDTYPE,
      9                         CARDID,
     10                         SUM(NUMBERS) OVER(PARTITION BY CARDTYPE) "COUNTS",
     11                         TO_NUMBER(CARDID) -
     12                         (ROW_NUMBER()
     13                          OVER(PARTITION BY CARDTYPE ORDER BY CARDID)) DIF
     14                    FROM SCOTT.CARD)
     15           GROUP BY CARDTYPE, COUNTS, DIF) T1
     16   GROUP BY CARDTYPE, COUNTS;CARDTYPE       COUNTS STR_SUM
    ---------- ---------- --------------------------------------------------------------------------------
    A                   8 01001-01003,01007
    B                   8 05005-05006SQL> 
      

  11.   

    借用oracledbalgtu的WMSYS.WM_CONCAT方法,以下sql可实现SELECT CARDTYPE, SUM(SN), WMSYS.WM_CONCAT(CARDID) CARDID
      FROM (SELECT CARDTYPE,
                   SUM(NUMBERS) SN,
                   DECODE(TO_CHAR(MAX(CARDID)),
                          TO_CHAR(MIN(CARDID)),
                          TO_CHAR(MIN(CARDID)),
                          TO_CHAR(MIN(CARDID)) || '-' || TO_CHAR(MAX(CARDID))) CARDID
              FROM (SELECT CARDTYPE,
                           NUMBERS,
                           CARDID,
                           TO_NUMBER(CARDID) -
                           (row_number()
                            over(partition by CARDTYPE order by CARDID)) col
                      FROM T
                     order by CARDTYPE, col)
             GROUP BY CARDTYPE, COL
             ORDER BY 1, 3)
     GROUP BY CARDTYPE;本人测试了下,很好用,学到不少啊,呵~~