【DB笔试面试459】ORA-00904:_ORA-00904

         题目         部分

ORA-00904: "wm_concat":invalid identifier错误如何解决?


     
         答案部分          



若在创建数据库的时候没有创建WMSYS用户,则在SQLPL/SQL中有用到WM_CONCAT函数的时候就会报ORA-00904的错误。其实,WMSYS用户下的WM_CONCAT函数有很重要的用途,比如行转列,但是该函数不稳定。例如,在Oracle 10g上返回的是字符串类型,但是在Oracle 11gR2上返回的是CLOB类型。很多数据库开发人员在程序中使用了该函数,若是系统升级,则会导致程序出现错误为了减轻程序员修改程序的工作量,只有重建函数WM_CONCAT来解决该问题。

若没有创建WMSYS用户的话,则在查询DBA_OBJECTS视图的时候就不能查询到WM_CONCAT的相关信息。正常情况下查询DBA_OBJECTS视图,会有如下的信息:

1 SQL> SELECT * FROM DBA_OBJECTS WHERE OBJECT_NAME LIKE 'WM_CONCAT%';
     


【DB笔试面试459】ORA-00904:_ORA-00904_02


解决办法有两种,一种是采用Oracle本身的脚本来创建WM_CONCAT函数,一种是采用自己创建的函数来解决这个问题。

1、用Oracle自带脚本重建WMSYS用户的WMSYS.WM_CONCAT函数

运行如下脚本卸载WMSYS用户的数据:

1@$ORACLE_HOME/rdbms/admin/owmuinst.plb
     

运行如下脚本安装WMSYS用户即可创建WMSYS.WM_CONCAT函数:

1@$ORACLE_HOME/rdbms/admin/owminst.plb
     


解锁WMSYS用户:

1ALTER USER WMSYS ACCOUNT UNLOCK;
     


2、自己订制脚本

如果只是单个用户使用,那么不用刻意去创建WMSYS用户,可以在所需的用户下运行订制脚本,生成WM_CONCAT函数。另外,为了和系统的函数名区别开来,也可以修改函数名称。如果是多个用户使用,也可以运行自己定制的脚本,然后创建同义词,这样多个用户都可以使用。

下面按照返回值的不同分为几种情况来订制不同的脚本。

① 无分隔符,返回CLOB类型

创建函数的脚本如下所示:

 1CREATE OR REPLACE TYPE WM_CONCAT_IMPL_CLOB_NULL_LHR AUTHID CURRENT_USER AS OBJECT
2(
3  CURR_STR CLOB,
4  STATIC FUNCTION ODCIAGGREGATEINITIALIZE(SCTX IN OUT WM_CONCAT_IMPL_CLOB_NULL_LHR)
5    RETURN NUMBER,
6  MEMBER FUNCTION ODCIAGGREGATEITERATE(SELF IN OUT WM_CONCAT_IMPL_CLOB_NULL_LHR,
7                                       P1   IN CLOBRETURN NUMBER,
8  MEMBER FUNCTION ODCIAGGREGATETERMINATE(SELF        IN WM_CONCAT_IMPL_CLOB_NULL_LHR,
9                                         RETURNVALUE OUT CLOB,
10                                         FLAGS       IN NUMBER)
11    RETURN NUMBER,
12  MEMBER FUNCTION ODCIAGGREGATEMERGE(SELF  IN OUT WM_CONCAT_IMPL_CLOB_NULL_LHR,
13                                     SCTX2 IN WM_CONCAT_IMPL_CLOB_NULL_LHR)
14    RETURN NUMBER
15);
16/
17CREATE OR REPLACE TYPE BODY WM_CONCAT_IMPL_CLOB_NULL_LHR IS
18  STATIC FUNCTION ODCIAGGREGATEINITIALIZE(SCTX IN OUT WM_CONCAT_IMPL_CLOB_NULL_LHR)
19    RETURN NUMBER IS
20  BEGIN
21    SCTX := WM_CONCAT_IMPL_CLOB_NULL_LHR(NULL);
22    RETURN ODCICONST.SUCCESS;
23  END;
24  MEMBER FUNCTION ODCIAGGREGATEITERATE(SELF IN OUT WM_CONCAT_IMPL_CLOB_NULL_LHR,
25                                       P1   IN CLOB) RETURN NUMBER IS
26  BEGIN
27    IF (CURR_STR IS NOT NULLTHEN
28      CURR_STR := CURR_STR ||  P1;
29    ELSE
30      CURR_STR := P1;
31    END IF;
32    RETURN ODCICONST.SUCCESS;
33  END;
34  MEMBER FUNCTION ODCIAGGREGATETERMINATE(SELF        IN WM_CONCAT_IMPL_CLOB_NULL_LHR,
35                                         RETURNVALUE OUT CLOB,
36                                         FLAGS       IN NUMBER) RETURN NUMBER IS
37  BEGIN
38    RETURNVALUE := CURR_STR;
39    RETURN ODCICONST.SUCCESS;
40  END;
41  MEMBER FUNCTION ODCIAGGREGATEMERGE(SELF  IN OUT WM_CONCAT_IMPL_CLOB_NULL_LHR,
42                                     SCTX2 IN WM_CONCAT_IMPL_CLOB_NULL_LHR)
43    RETURN NUMBER IS
44  BEGIN
45    IF (SCTX2.CURR_STR IS NOT NULLTHEN
46      SELF.CURR_STR := SELF.CURR_STR ||  SCTX2.CURR_STR;
47    END IF;
48    RETURN ODCICONST.SUCCESS;
49  END;
50END;
51/
52CREATE OR REPLACE FUNCTION WM_CONCAT_CLOB_NULL_LHR(P1 VARCHAR2) RETURN CLOB
53  AGGREGATE USING WM_CONCAT_IMPL_CLOB_NULL_LHR;
54/
55CREATE PUBLIC SYNONYM WM_CONCAT_CLOB_NULL_LHR FOR WM_CONCAT_CLOB_NULL_LHR;
56GRANT EXECUTE ON WM_CONCAT_CLOB_NULL_LHR TO PUBLIC;
     


以上函数的测试示例如下所示,函数的返回值是无分隔符的CLOB,在PL/SQL中要使用TO_CHAR进行转换:

1SYS@lhrdb21> SELECT D.USER_ID FROM DBA_USERS D WHERE D.USER_ID IN (05);
2   USER_ID
3----------
4         0
5         5
6SYS@lhrdb21> SELECT WM_CONCAT_CLOB_NULL_LHR(D.USER_ID) FROM DBA_USERS D WHERE D.USER_ID IN (05);
7WM_CONCAT_CLOB_LHR_NULL(D.USER_ID)
8--------------------------------------------------------------------------------
905
     


② 逗号分隔符,返回CLOB

创建函数的脚本如下所示:

 1CREATE OR REPLACE TYPE WM_CONCAT_IMPL_CLOB_LHR AUTHID CURRENT_USER AS OBJECT
2(
3  CURR_STR CLOB,
4  STATIC FUNCTION ODCIAGGREGATEINITIALIZE(SCTX IN OUT WM_CONCAT_IMPL_CLOB_LHR)
5    RETURN NUMBER,
6  MEMBER FUNCTION ODCIAGGREGATEITERATE(SELF IN OUT WM_CONCAT_IMPL_CLOB_LHR,
7                                       P1   IN CLOBRETURN NUMBER,
8  MEMBER FUNCTION ODCIAGGREGATETERMINATE(SELF        IN WM_CONCAT_IMPL_CLOB_LHR,
9                                         RETURNVALUE OUT CLOB,
10                                         FLAGS       IN NUMBER)
11    RETURN NUMBER,
12  MEMBER FUNCTION ODCIAGGREGATEMERGE(SELF  IN OUT WM_CONCAT_IMPL_CLOB_LHR,
13                                     SCTX2 IN WM_CONCAT_IMPL_CLOB_LHR)
14    RETURN NUMBER
15);
16/
17CREATE OR REPLACE TYPE BODY WM_CONCAT_IMPL_CLOB_LHR IS
18  STATIC FUNCTION ODCIAGGREGATEINITIALIZE(SCTX IN OUT WM_CONCAT_IMPL_CLOB_LHR)
19    RETURN NUMBER IS
20  BEGIN
21    SCTX := WM_CONCAT_IMPL_CLOB_LHR(NULL);
22    RETURN ODCICONST.SUCCESS;
23  END;
24  MEMBER FUNCTION ODCIAGGREGATEITERATE(SELF IN OUT WM_CONCAT_IMPL_CLOB_LHR,
25                                       P1   IN CLOB) RETURN NUMBER IS
26  BEGIN
27    IF (CURR_STR IS NOT NULLTHEN
28      CURR_STR := CURR_STR || ',' || P1;
29    ELSE
30      CURR_STR := P1;
31    END IF;
32    RETURN ODCICONST.SUCCESS;
33  END;
34  MEMBER FUNCTION ODCIAGGREGATETERMINATE(SELF        IN WM_CONCAT_IMPL_CLOB_LHR,
35                                         RETURNVALUE OUT CLOB,
36                                         FLAGS       IN NUMBER) RETURN NUMBER IS
37  BEGIN
38    RETURNVALUE := CURR_STR;
39    RETURN ODCICONST.SUCCESS;
40  END;
41  MEMBER FUNCTION ODCIAGGREGATEMERGE(SELF  IN OUT WM_CONCAT_IMPL_CLOB_LHR,
42                                     SCTX2 IN WM_CONCAT_IMPL_CLOB_LHR)
43    RETURN NUMBER IS
44  BEGIN
45    IF (SCTX2.CURR_STR IS NOT NULLTHEN
46      SELF.CURR_STR := SELF.CURR_STR || ',' || SCTX2.CURR_STR;
47    END IF;
48    RETURN ODCICONST.SUCCESS;
49  END;
50END;
51/
52CREATE OR REPLACE FUNCTION WM_CONCAT_CLOB_LHR(P1 VARCHAR2) RETURN CLOB
53  AGGREGATE USING WM_CONCAT_IMPL_CLOB_LHR;
54/
55CREATE PUBLIC SYNONYM WM_CONCAT_CLOB_LHR FOR WM_CONCAT_CLOB_LHR;
56GRANT EXECUTE ON WM_CONCAT_CLOB_LHR TO PUBLIC;
     


以上函数的测试示例如下所示,函数的返回值是以逗号为分隔符的CLOB,在PL/SQL中需要使用TO_CHAR进行转换:

1SYS@lhrdb21> SELECT D.USER_ID FROM DBA_USERS D WHERE D.USER_ID IN (05);
2   USER_ID
3----------
4         0
5         5
6SYS@lhrdb21> SELECT WM_CONCAT_CLOB_LHR(D.USER_ID) FROM DBA_USERS D WHERE D.USER_ID IN (05);
7WM_CONCAT_LHR(D.USER_ID)
8--------------------------------------------------------------------------------
90,5
     


③ 逗号分隔符,返回字符串类型

创建函数的脚本如下所示:

 1CREATE OR REPLACE TYPE WM_CONCAT_IMPL_STRINGS_LHR AUTHID CURRENT_USER AS OBJECT
2(
3  CURR_STR VARCHAR2(32767),
4  STATIC FUNCTION ODCIAGGREGATEINITIALIZE(SCTX IN OUT WM_CONCAT_IMPL_STRINGS_LHR)
5    RETURN NUMBER,
6  MEMBER FUNCTION ODCIAGGREGATEITERATE(SELF IN OUT WM_CONCAT_IMPL_STRINGS_LHR,
7                                       P1   IN VARCHAR2) RETURN NUMBER,
8  MEMBER FUNCTION ODCIAGGREGATETERMINATE(SELF        IN WM_CONCAT_IMPL_STRINGS_LHR,
9                                         RETURNVALUE OUT VARCHAR2,
10                                         FLAGS       IN NUMBER)
11    RETURN NUMBER,
12  MEMBER FUNCTION ODCIAGGREGATEMERGE(SELF  IN OUT WM_CONCAT_IMPL_STRINGS_LHR,
13                                     SCTX2 IN WM_CONCAT_IMPL_STRINGS_LHR)
14    RETURN NUMBER
15);
16/
17CREATE OR REPLACE TYPE BODY WM_CONCAT_IMPL_STRINGS_LHR IS
18  STATIC FUNCTION ODCIAGGREGATEINITIALIZE(SCTX IN OUT WM_CONCAT_IMPL_STRINGS_LHR)
19    RETURN NUMBER IS
20  BEGIN
21    SCTX := WM_CONCAT_IMPL_STRINGS_LHR(NULL);
22    RETURN ODCICONST.SUCCESS;
23  END;
24  MEMBER FUNCTION ODCIAGGREGATEITERATE(SELF IN OUT WM_CONCAT_IMPL_STRINGS_LHR,
25                                       P1   IN VARCHAR2) RETURN NUMBER IS
26  BEGIN
27    IF (CURR_STR IS NOT NULLTHEN
28      CURR_STR := CURR_STR || ',' || P1;
29    ELSE
30      CURR_STR := P1;
31    END IF;
32    RETURN ODCICONST.SUCCESS;
33  END;
34  MEMBER FUNCTION ODCIAGGREGATETERMINATE(SELF        IN WM_CONCAT_IMPL_STRINGS_LHR,
35                                         RETURNVALUE OUT VARCHAR2,
36                                         FLAGS       IN NUMBER) RETURN NUMBER IS
37  BEGIN
38    RETURNVALUE := CURR_STR;
39    RETURN ODCICONST.SUCCESS;
40  END;
41  MEMBER FUNCTION ODCIAGGREGATEMERGE(SELF  IN OUT WM_CONCAT_IMPL_STRINGS_LHR,
42                                     SCTX2 IN WM_CONCAT_IMPL_STRINGS_LHR)
43    RETURN NUMBER IS
44  BEGIN
45    IF (SCTX2.CURR_STR IS NOT NULLTHEN
46      SELF.CURR_STR := SELF.CURR_STR || ',' || SCTX2.CURR_STR;
47    END IF;
48    RETURN ODCICONST.SUCCESS;
49  END;
50END;
51/
52CREATE OR REPLACE FUNCTION WM_CONCAT_STRINGS_LHR(P1 VARCHAR2) RETURN VARCHAR2
53  AGGREGATE USING WM_CONCAT_IMPL_STRINGS_LHR;
54/
55CREATE PUBLIC SYNONYM WM_CONCAT_STRINGS_LHR FOR WM_CONCAT_STRINGS_LHR;
56GRANT EXECUTE ON WM_CONCAT_STRINGS_LHR TO PUBLIC;
     


以上函数的测试示例如下所示,函数的返回值是以逗号为分隔符的字符串:

1SYS@lhrdb21> SELECT D.USER_ID FROM DBA_USERS D WHERE D.USER_ID IN (05);
2   USER_ID
3----------
4         0
5         5
6SYS@lhrdb21> SELECT WM_CONCAT_STRINGS_LHR(D.USER_ID) FROM DBA_USERS D WHERE D.USER_ID IN (05);
7WM_CONCAT_STRINGS_LHR(D.USER_ID)
8---------------------------------------------------
90,5
     


其实,与WM_CONCAT相似的还有一个函数是LISTAGG这是一个Oracle的列转行函数,使用示例如下所示:

 1WITH TEMP AS(
2  SELECT 'China' NATION ,'Guangzhou' CITY FROM DUAL UNION ALL
3  SELECT 'China' NATION ,'Shanghai' CITY FROM DUAL UNION ALL
4  SELECT 'China' NATION ,'Beijing' CITY FROM DUAL UNION ALL
5  SELECT 'USA' NATION ,'New York' CITY FROM DUAL UNION ALL
6  SELECT 'USA' NATION ,'Bostom' CITY FROM DUAL UNION ALL
7  SELECT 'USA' NATION ,'Bostom' CITY FROM DUAL UNION ALL
8  SELECT 'Japan' NATION ,'Tokyo' CITY FROM DUAL
9)
10SELECT NATION,LISTAGG(CITY,','WITHIN GROUP (ORDER BY CITY)
11FROM TEMP
12GROUP BY NATION;
     


输出结果如下所示:

【DB笔试面试459】ORA-00904:_ORA-00904_03

 

对于LISTAGG函数,如果聚合的内容太多就会报“ORA-01489: result of string concatenation is too long”的错误,那么这个时候可以从业务的角度去修改SQL,也可以使用WM_CONCAT函数返回CLOB类型来解决这个问题。