【问题标题】:ORACLE/SQL: wm_concat & order byORACLE/SQL: wm_concat & order by
【发布时间】:2011-02-11 15:00:33
【问题描述】:

我正在通过 ODBC 和水晶报告 2008 使用 oracle 11(不确定确切的版本,但由于 LISTAGG 不起作用,我想它不是版本 2)。

这是我遇到的问题:

这是一张桌子:

TABLE ODB.TASK_CARD_CONTROL  
------------------------------------------  
task_card     control_category     code  
------------------------------------------  
1                  zone             17  
1                  zone             33  
1                  zone             21  
2                  zone             18  
2                  zone             05  
3                  zone             55  
3                  zone             32  
3                  zone             72 

我正在使用 WM_CONCAT 函数来获得这样的东西:

task_card      zones
1              17,33,21
2              18,05
3              55,32,72

下面是 SQL:

SELECT TASK_CARD, WM_CONCAT(code) as ZONES
FROM ODB.TASK_CARD_CONTROL
WHERE ODB.TASK_CARD_CONTROL.CONTROL_CATEGORY = 'ZONE'
GROUP BY TASK_CARD

但我想对区域进行排序,所以我尝试了这个:

SELECT TASK_CARD, WM_CONCAT(code) as ZONES
FROM (SELECT TASK_CARD, CODE, CONTROL_CATEGORY FROM ODB.TASK_CARD_CONTROL 
ORDER BY CODE)
WHERE ODB.TASK_CARD_CONTROL.CONTROL_CATEGORY = 'ZONE'
GROUP BY TASK_CARD

但由于某种原因,它返回以下错误:

Failed to retrieve data from the database.
Details: 42S22:[Oracle][ODBC][Ora]ORA-00904: 
"ODB"."TASK_CARD_CONTROL"."CONTROL_CATEGORY" : invalid identifier

我真的不明白我在这里做错了什么......有人可以给我提示吗?

【问题讨论】:

    标签: sql oracle oracle11g wm-concat


    【解决方案1】:

    对于仍在使用 wm_CONCAT(又名旧 db 版本)的任何人: 解决方案是添加不同的条件,然后它也会对连接的值应用升序。

    不要问为什么它没有记录在案,但它会起作用。

    另外,在子查询中使用 order by,在 wm_concat 之前只会随机化顺序,所以不应该推荐它。

    请求的 SQL 示例:

    SELECT TASK_CARD, WM_CONCAT(distinct code) as ZONES
    FROM ODB.TASK_CARD_CONTROL
    WHERE ODB.TASK_CARD_CONTROL.CONTROL_CATEGORY = 'ZONE'
    GROUP BY TASK_CARD;
    

    请注意,在过程/包中使用时 distinct 选项不起作用。

    【讨论】:

      【解决方案2】:

      您不能从内部查询外部引用 ODB.TASK_CARD_CONTROL.CONTROL_CATEGORY。试试:

      SELECT TASK_CARD, WM_CONCAT(code) as ZONES
      FROM (SELECT TASK_CARD, CODE, CONTROL_CATEGORY FROM ODB.TASK_CARD_CONTROL 
            WHERE ODB.TASK_CARD_CONTROL.CONTROL_CATEGORY = 'ZONE'
            ORDER BY CODE)
      GROUP BY TASK_CARD
      

      【讨论】:

      • 好吧,我在此期间也想过;我试过这个: SELECT TASK_CARD, WM_CONCAT(code) as ZONES FROM (SELECT TASK_CARD, CODE, FROM ODB.TASK_CARD_CONTROL WHERE ODB.TASK_CARD_CONTROL.CONTROL_CATEGORY = 'ZONE' ORDER BY CODE) GROUP BY TASK_CARD - 但在选择中省略了 CONTROL_CATEGORY给了我一个错误。将其添加回您的查询中,现在似乎可以正常工作了!谢谢:)
      • @LalitKumarB 你为什么问我?我正在回答一个(顺便)使用 WM_CONCAT 的问题,而不是提倡使用它!
      • @LalitKumarB 没问题!
      【解决方案3】:

      如果你在 from 子句中给子查询一个名字,你就可以引用子查询本身的列

      SELECT t1.TASK_CARD
      , WM_CONCAT(t1.code) as ZONES
      FROM 
      (SELECT TASK_CARD, CODE, CONTROL_CATEGORY FROM ODB.TASK_CARD_CONTROL ORDER BY CODE) t1
      WHERE t1.CONTROL_CATEGORY = 'ZONE'
      GROUP BY t1.TASK_CARD
      

      【讨论】:

      • 即使没有子查询别名,您也可以这样做,因为接受的答案显示
      【解决方案4】:
      1. 按所需列排序,然后
      2. 在外部查询中按行号排序。
      3. 使用函数。

      这个函数有最后一个rownum顺序的逻辑:

      Select wmsys.wm_concat(t) CONCAT from 
      (
          Select t from (
              Select t from (
                  Select 'aa' t from dual
                  union
                  Select 'zz' t from dual
                  union
                  Select 'pp' t from dual
                  union
                  Select 'll' t from dual
                  union
                  Select 'mm' t from dual
                  union
                  Select 'xx' t from dual
                  union
                  Select 'cc' t from dual
              ) a 
              order by t
          ) order by rownum
      ) t
      

      【讨论】:

        【解决方案5】:

        LISTAGG 是在 11g 第 2 版 中引入的。

        因此,在不支持LISTAGG11g 之前的Oracle 版本 中,您可以使用ROW_NUMBER()SYS_CONNECT_BY_PATH 函数。

        Oracle String Aggregation Techniques

        SELECT task_card,
          LTRIM(MAX(SYS_CONNECT_BY_PATH(code,','))
          KEEP (DENSE_RANK LAST ORDER BY curr),',') AS zones
          FROM   (SELECT task_card,
                        code,
                        ROW_NUMBER() OVER (PARTITION BY fruit ORDER BY code) AS curr,
                        ROW_NUMBER() OVER (PARTITION BY fruit ORDER BY code) -1 AS prev
                 FROM   table_name)
          GROUP BY task_card
          CONNECT BY prev = PRIOR curr AND task_card= PRIOR task_card
         START WITH curr = 1;
        

        注意

        永远不要使用WM_CONCAT,因为它是一个未记录的功能,并且已从 12c 版本中删除。

        任何一直依赖wm_concat 功能的应用程序一旦升级到12c 将无法运行。从那以后,它已被删除。见Why not use WM_CONCAT function in Oracle?

        SQL> select banner from v$version where rownum = 1;
        
        BANNER
        ----------------------------------------------------------------------------
        Oracle Database 12c Enterprise Edition Release 12.1.0.1.0 - 64bit Production
        
        SQL> SELECT object_name
          2  FROM dba_objects
          3  WHERE owner='WMSYS'
          4  AND object_name LIKE 'WM\_%' ESCAPE '\';
        
        OBJECT_NAME
        ----------------------------------------------------------------------------
        WM_REPLICATION_INFO
        WM_RDIFF
        WM_PERIOD
        WM_PERIOD
        WM_OVERLAPS
        WM_MEETS
        WM_LESSTHAN
        WM_LDIFF
        WM_INTERSECTION
        WM_INSTALLATION
        WM_GREATERTHAN
        WM_EVENTS_INFO
        WM_ERROR
        WM_ERROR
        WM_EQUALS
        WM_DDL_UTIL
        WM_DDL_UTIL
        WM_CONTAINS
        WM_COMPRESS_BATCH_SIZES
        WM_COMPRESSIBLE_TABLES
        
        20 rows selected.
        
        SQL>
        

        您将收到“invalid identifier”错误:

        SQL> SELECT banner FROM v$version;
        
        BANNER
        ----------------------------------------------------------------------------
        Oracle Database 12c Enterprise Edition Release 12.1.0.1.0 - 64bit Production
        PL/SQL Release 12.1.0.1.0 - Production
        CORE    12.1.0.1.0      Production
        TNS for 64-bit Windows: Version 12.1.0.1.0 - Production
        NLSRTL Version 12.1.0.1.0 - Production
        
        SQL> SELECT deptno, wm_concat(ename) FROM emp;
        SELECT deptno, wm_concat(ename) FROM emp
                       *
        ERROR at line 1:
        ORA-00904: "WM_CONCAT": invalid identifier
        

        因此,没有必要依赖最新版本中不再提供的未记录的功能

        【讨论】:

          【解决方案6】:

          使用 ListAgg 代替 wm_concat

          SELECT TASK_CARD, ListAgg(code) within (order by code asc) as ZONES
          

          http://nimishgarg.blogspot.com/2010/07/oracle-differece-between-wmconcat-and.html

          【讨论】:

          • OP 明确表示不能使用listagg。另外,它是WITHIN GROUP
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2011-11-20
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多