ORA-12899: value too large for column lors du rafraîchissement d’une vue matérialisée

La longueur des colonnes d’une vue matérialisée est définie lors de la création de la vue matérialisée. Si la longueur d’une colonne d’une table utilisée dans la vue matérialisée change, la taille de la colonne correspondante dans la vue matérialisée n’est pas modifiée ce qui peut conduire à une erreur ORA-12899 si la longueur a été augmentée.

Exemple :

SQL> create table t(x varchar2(4));

Table T created.

SQL> create materialized view mv as select * from t;

Materialized view MV created.

SQL> desc mv
Name Null? Type 
---- ----- ----------- 
X          VARCHAR2(4) 

SQL> select object_name,object_type,status from user_objects where object_name = 'MV';

OBJECT_NAME     OBJECT_TYPE             STATUS 
--------------- ----------------------- -------
MV              TABLE                   VALID 
MV              MATERIALIZED VIEW       VALID

SQL> select mview_name,staleness,compile_state from user_mviews where mview_name = 'MV';

MVIEW_NAME      STALENESS           COMPILE_STATE 
--------------- ------------------- -------------------
MV              FRESH               VALID

SQL> alter table t modify x varchar2(8);

Table T altered.

SQL> desc mv
Name Null? Type 
---- ----- ----------- 
X VARCHAR2(4) 

SQL> select object_name,object_type,status from user_objects where object_name = 'MV';

OBJECT_NAME     OBJECT_TYPE             STATUS 
--------------- ----------------------- -------
MV              TABLE                   VALID 
MV              MATERIALIZED VIEW       INVALID

SQL> select mview_name,staleness,compile_state from user_mviews where mview_name = 'MV';

MVIEW_NAME      STALENESS           COMPILE_STATE 
--------------- ------------------- -------------------
MV              NEEDS_COMPILE       NEEDS_COMPILE

SQL> insert into t(x) values ('Olivier');

1 row inserted.

SQL> commit;

Commit complete.

SQL> exec dbms_mview.refresh('mv')

Error starting at line : 1 in command -
BEGIN dbms_mview.refresh('mv'); END;
Error report -
ORA-12008: error in materialized view refresh path
ORA-12899: value too large for column "OHEURTEL"."MV"."X" (actual: 7, maximum: 4)
ORA-06512: at "SYS.DBMS_SNAPSHOT", line 2821
ORA-06512: at "SYS.DBMS_SNAPSHOT", line 3058
ORA-06512: at "SYS.DBMS_SNAPSHOT", line 3017
ORA-06512: at line 1

SQL> desc mv
Name Null? Type 
---- ----- ----------- 
X          VARCHAR2(4) 

SQL> select object_name,object_type,status from user_objects where object_name = 'MV';

OBJECT_NAME     OBJECT_TYPE             STATUS 
--------------- ----------------------- -------
MV              TABLE                   VALID 
MV              MATERIALIZED VIEW       INVALID

SQL> select mview_name,staleness,compile_state from user_mviews where mview_name = 'MV';

MVIEW_NAME      STALENESS           COMPILE_STATE 
--------------- ------------------- -------------------
MV              COMPILATION_ERROR   COMPILATION_ERROR 

Comme le montre cet exemple, après avoir augmenté la longueur d’une colonne de la table sous-jacente, la vue matérialisée a été marquée INVALID et  NEEDS_COMPILE (à recompiler). Lors du rafraîchissement de la vue matérialisée, Oracle a tenté de recompiler la vue mais sans succès ; la longueur de la colonne dans la vue matérialisée est restée inchangée ce qui a provoqué l’erreur ORA-12899.

Pour résoudre ce problème, le plus simple, sans reconstruire complètement la vue matérialisée, consiste à modifier aussi la longueur de la colonne dans la vue matérialisée.

Exemple : 

SQL> alter materialized view mv modify x varchar2(8);

Materialized view MV altered.

SQL> exec dbms_mview.refresh('mv')

PL/SQL procedure successfully completed.

SQL> desc mv
Name Null? Type        
---- ----- ----------- 
X          VARCHAR2(8) 

SQL> select object_name,object_type,status from user_objects where object_name = 'MV';

OBJECT_NAME     OBJECT_TYPE             STATUS 
--------------- ----------------------- -------
MV              TABLE                   VALID  
MV              MATERIALIZED VIEW       VALID  

SQL> select mview_name,staleness,compile_state from user_mviews where mview_name = 'MV';

MVIEW_NAME      STALENESS           COMPILE_STATE      
--------------- ------------------- -------------------
MV              FRESH               VALID              

Patch des bases de données antérieures à la version 11.2.0.3 avant avril 2019

Une note récente sur le site du support Oracle (2335265.1 – Mandatory Patching Requirement for Database Versions 11.2.0.3 or Earlier, Using DB Links) indique que les bases de données Oracle antérieures à la version 11.2.0.3 devront avoir un niveau de patch minimum avant avril 2019 pour assurer le bon fonctionnement des database links.

Les bases de données en versions 12.2.0.1 et ultérieures, 11.2.0.4 et 12.1.0.2 ne sont pas concernées car elles incorporent déjà le correctif.
Pour les versions précédentes (12.1.0.1, 11.2.0.3 et 11.1.0.7), un niveau minimum de patch précisé dans la note du support Oracle doit être appliqué. Pour les versions encore plus anciennes, une mise à niveau est requise.

A priori, le patch ou la mise à niveau sera nécessaire uniquement pour continuer à utiliser des database links avec les dernières versions de la base de données ou avec des versions patchées. Normalement, un database link entre deux bases de données anciennes ou non patchées devrait continuer à fonctionner sans problème.

Pour toute question, il ne faut pas hésiter à contacter le support Oracle ou consulter le forum dédié à ce sujet (https://community.oracle.com/message/14710245#14710245).

Oracle Database 18c

Oracle 18c

Annoncée lors du dernier Oracle Open World en octobre 2017, la version Oracle Database 18c est disponible depuis la fin de la semaine dernière dans Oracle Cloud et dans les Engineered Systems Oracle (les serveurs commercialisés par Oracle). Pour la version on-premise, installable sur vos propres serveurs, il faudra attendre un peu (2ème semestre 2018).

Oracle Database 18c est la première version de la base de données qui utilise une numérotation de version annuelle. Malgré le grand saut dans la numérotation des versions, cette nouvelle version n’est pas une version majeure mais l’équivalent d’une version 12.2.0.2. Elle apporte néanmoins un certain nombre de nouveautés que vous pouvez découvrir dans la documentation :

Pour les impatients, il est possible de commencer à jouer avec Oracle Database 18c grâce à Oracle Live SQL, un environnement Web gratuit qui permet de tester du code SQL et PL/SQL :

Oracle Live SQL

En parcourant rapidement la documentation, j’ai vu quelques nouveautés intéressantes dans les fonctionnalités relatives au développement :

  • Table temporaire privée
    • Table temporaire stockée en mémoire visible uniquement par la session qui l’a créée
  • Possibilité de réinitialiser une séquence à une valeur donnée (annoncé dans la documentation mais visiblement pas implémenté)
  • Fonction table polymorphique
    • Fonction table dont la structure de retour (liste des colonnes, type de données des colonnes) peut changer en fonction de l’appel
  • Expressions qualifiées dans l’affectation de valeurs à un enregistrement PL/SQL (RECORD) ou l’initialisation d’une collection.
    Exemple :
declare
 type type_table_chiffres is table of varchar2(10) index by pls_integer;
 t_impair type_table_chiffres;
begin
 -- Nouvelle notation pour initialiser la collection.
 t_impair := type_table_chiffres(1 => 'Un', 3 => 'Trois', 5 => 'Cinq');
end;
/

ORA-54032: column to be renamed is used in a virtual column expression

Récemment, deux clients différents en 12.1.0.2 ont rencontré l’erreur ORA-54032 en tentant de renommer une colonne d’une table, alors qu’il n’y avait pas de colonne virtuelle explicite qui utilisait la colonne à renommer.

En version 12.1.0.1 et 12.1.0.2, Oracle considère qu’il s’agit d’un bug (Bug 21070487 – ORA-54032: column to be renamed is used in a virtual column expression (Doc ID 21070487.8)) mais cette erreur se produisait déjà en 11.2.0.4.

Exemple :

SQL> create table t
  2    (
  3    x number,
  4    y number,
  5    z number as (x+y)
  6    );

Table T created.

SQL> select column_name,data_default from user_tab_columns where table_name = 'T';

COLUMN_NAME  DATA_DEFAULT
------------ ------------
X                        
Y                        
Z            "X"+"Y"     

SQL> alter table t rename column x to w;

Error starting at line : 15 in command -
alter table t rename column x to w
Error report -
ORA-54032: column to be renamed is used in a virtual column expression
54032. 0000 -  "column to be renamed is used in a virtual column expression"
*Cause:    Attempted to rename a column that was used in a virtual column
           expression.
*Action:   Drop the virtual column first or change the virtual column
           expression to eliminate dependency on the column to be renamed.

Il est intéressant de noter que le problème ne se pose pas avec les expressions utilisées dans un Function Based Index :

SQL> create table t
  2    (
  3    x number,
  4    y number
  5    );

Table T created.

SQL> create index i on t(x+y);

Index I created.

SQL> select column_name,data_default from user_tab_cols 
  2  where table_name = 'T';

COLUMN_NAME  DATA_DEFAULT
------------ ------------
X                        
Y                        
SYS_NC00003$ "X"+"Y"     

SQL> select column_expression from user_ind_expressions 
  2  where table_name = 'T' and index_name = 'I';

COLUMN_EXPRESSION 
--------------------
"X"+"Y"

SQL> alter table t rename column x to w;

Table T altered.

SQL> select column_name,data_default from user_tab_cols 
  2  where table_name = 'T';

COLUMN_NAME  DATA_DEFAULT
------------ ------------
X                        
Y                        
SYS_NC00003$ "W"+"Y"     

SQL> select column_expression from user_ind_expressions 
  2  where table_name = 'T' and index_name = 'I';

COLUMN_EXPRESSION 
--------------------
"W"+"Y"

La colonne a bien été renommée par Oracle dans l’expression.

En 12.1, la situation est aggravée par le fait que la collecte automatique des statistiques crée des colonnes virtuelles afin d’obtenir des statistiques plus précises lorsqu’il y a une corrélation entre deux colonnes utilisées simultanément dans une clause WHERE. Ces statistiques étendues permettent normalement à l’optimiseur de choisir de meilleurs plans d’exécution, ce qui est a priori une bonne chose.

Mais du coup, les colonnes impliquées dans ces statistiques ne peuvent plus être renommées ; c’est le problème rencontré chez mes deux clients.

Exemple :

SQL> create table t
  2    (
  3    x number,
  4    y number
  5    );

Table T created.

SQL> begin
  2    dbms_stats.gather_table_stats
  3      (
  4      ownname => null,
  5      tabname => 't',
  6      method_opt => 'for columns (x,y) size auto'
  7      );
  8  end;
  9  /

PL/SQL procedure successfully completed.

SQL> select column_name,data_default from user_tab_cols 
  2  where table_name = 'T';

COLUMN_NAME                    DATA_DEFAULT                  
------------------------------ ------------------------------
X                                                            
Y                                                            
SYS_STUYPW88OE302TFVBNC6$MMQXE SYS_OP_COMBINED_HASH("X","Y") 

SQL> select extension_name, extension from user_stat_extensions 
  2  where table_name = 'T';

EXTENSION_NAME                 EXTENSION                     
------------------------------ ------------------------------
SYS_STUYPW88OE302TFVBNC6$MMQXE ("X","Y")                     

SQL> alter table t rename column x to w;

Error starting at line : 52 in command -
alter table t rename column x to w
Error report -
ORA-54032: column to be renamed is used in a virtual column expression
54032. 0000 -  "column to be renamed is used in a virtual column expression"
*Cause:    Attempted to rename a column that was used in a virtual column
           expression.
*Action:   Drop the virtual column first or change the virtual column
           expression to eliminate dependency on the column to be renamed.

Ce problème est corrigé en 12.2, que ce soit pour les colonnes virtuelles créées explicitement dans la table ou implicitement par Oracle lors de la collecte des statistiques. Ce problème est aussi corrigé en 12.1.0.2 par un patch disponible pour différentes plate-formes.

Sinon, il est possible de contourner le bug, en supprimant temporairement la colonne virtuelle explicite ou les statistiques étendues, le temps de renommer la colonne.

Exemple avec une colonne virtuelle explicite :

SQL> create table t
  2    (
  3    x number,
  4    y number,
  5    z number as (x+y)
  6    );

Table T created.

SQL> alter table t rename column x to w;

Error starting at line : 86 in command -
alter table t rename column x to w
Error report -
ORA-54032: column to be renamed is used in a virtual column expression
54032. 0000 -  "column to be renamed is used in a virtual column expression"
*Cause:    Attempted to rename a column that was used in a virtual column
           expression.
*Action:   Drop the virtual column first or change the virtual column
           expression to eliminate dependency on the column to be renamed.
SQL> select column_name,data_default from user_tab_columns where table_name = 'T';

COLUMN_NAME                    DATA_DEFAULT                  
------------------------------ ------------------------------
X                                                            
Y                                                            
Z                              "X"+"Y"                       

SQL> alter table t modify z as (0); -- expression provisoire

Table T altered.

SQL> alter table t rename column x to w; 

Table T altered.

SQL> alter table t modify z as (w+y); -- expression redéfinie avec le nouveau nom

Table T altered.

SQL> select column_name,data_default from user_tab_columns where table_name = 'T';

COLUMN_NAME                    DATA_DEFAULT                  
------------------------------ ------------------------------
W                                                            
Y                                                            
Z                              "W"+"Y"

Exemple avec des statistiques étendues :

SQL> create table t
 2 (
 3 x number,
 4 y number
 5 );

Table T created.

SQL> begin
 2 dbms_stats.gather_table_stats
 3 (
 4 ownname => null,
 5 tabname => 't',
 6 method_opt => 'for columns (x,y) size auto'
 7 );
 8 end;
 9 /

PL/SQL procedure successfully completed.

SQL> alter table t rename column x to w;

Error starting at line : 112 in command -
alter table t rename column x to w
Error report -
ORA-54032: column to be renamed is used in a virtual column expression
54032. 0000 - "column to be renamed is used in a virtual column expression"
*Cause: Attempted to rename a column that was used in a virtual column
 expression.
*Action: Drop the virtual column first or change the virtual column
 expression to eliminate dependency on the column to be renamed.
SQL> select column_name,data_default from user_tab_cols 
 2 where table_name = 'T';

COLUMN_NAME DATA_DEFAULT 
------------------------------ ------------------------------
X 
Y 
SYS_STUYPW88OE302TFVBNC6$MMQXE SYS_OP_COMBINED_HASH("X","Y")

SQL> select extension_name, extension from user_stat_extensions 
 2 where table_name = 'T';

EXTENSION_NAME EXTENSION 
------------------------------ ------------------------------
SYS_STUYPW88OE302TFVBNC6$MMQXE ("X","Y")

SQL> begin
 2 dbms_stats.drop_extended_stats -- suppression des statistiques
 3 (
 4 ownname => null,
 5 tabname => 't',
 6 extension => '(x,y)'
 7 );
 8 end;
 9 /

PL/SQL procedure successfully completed.

SQL> alter table t rename column x to w;

Table T altered.

SQL> begin
 2 dbms_stats.gather_table_stats -- recréation des statistiques
 3 (
 4 ownname => null,
 5 tabname => 't',
 6 method_opt => 'for columns (w,y) size auto'
 7 );
 8 end;
 9 /

PL/SQL procedure successfully completed.

SQL> select column_name,data_default from user_tab_cols 
 2 where table_name = 'T';

COLUMN_NAME DATA_DEFAULT 
------------------------------ ------------------------------
W 
Y 
SYS_STU121KAA_EH6#$T1$TT0VE6RE SYS_OP_COMBINED_HASH("W","Y")

SQL> select extension_name, extension from user_stat_extensions 
 2 where table_name = 'T';

EXTENSION_NAME EXTENSION 
------------------------------ ------------------------------
SYS_STU121KAA_EH6#$T1$TT0VE6RE ("W","Y")

La collecte des statistiques étendues peut être désactivée à l’aide d’un paramètre caché (_optimizer_enable_extended_stats), mais ce serait dommage de se priver d’une fonctionnalité qui peut être bénéfique pour les performances.