Oracle
 sql >> Base de données >  >> RDS >> Oracle

Pourquoi Oracle ajoute-t-il une colonne masquée ici ?

Dans la version 11g d'Oracle, Oracle a introduit une nouvelle technique d'optimisation pour améliorer les performances des opérations DDL. Cette nouvelle fonctionnalité permet un temps d'exécution extrêmement rapide lors de l'ajout d'un NOT NULL colonne avec la valeur par défaut à une table existante. Depuis la version 12c, l'optimisation DDL a été étendue pour inclure NULL colonnes ayant la valeur par défaut.

Considérez le tableau de test suivant avec 1 000 000 lignes :

sql> create table xxy
as select rownum a from dual connect by level <= 1e6
;
sql> select /*+ gather_plan_statistics */ count(1) from xxy;
sql> select * from table(dbms_xplan.display_cursor); 

Nous allons maintenant ajouter une colonne supplémentaire non nulle ayant une valeur par défaut dans différentes sessions pour 11g et 12c :

11g> alter table xxy add b number default 1;
     --Table XXY altered. Elapsed: 00:01:00.998

12c> alter table xxy add b number default 1;
     --Table XXY altered. Elapsed: 00:00:00.052

Remarquez la différence dans le temps d'exécution :1M de lignes mises à jour en 5 ms !?

Le plan d'exécution indique :

11g> select count(1) from xxy where b = 1;
  COUNT(1)
----------
   1000000
11g> select * from table(dbms_xplan.display_cursor);
---------------------------------------------------------------------------
| Id  | Operation          | Name | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |      |       |       |  1040 (100)|          |
|   1 |  SORT AGGREGATE    |      |     1 |    13 |            |          |
|*  2 |   TABLE ACCESS FULL| XXY  |   898K|    11M|  1040   (1)| 00:00:13 |
---------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   2 - filter("B"=1)
Note
-----
   - dynamic sampling used for this statement (level=2)

12c> select count(1) from xxy where b = 1;
12c> select * from table(dbms_xplan.display_cursor);
---------------------------------------------------------------------------
| Id  | Operation          | Name | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |      |       |       |   429 (100)|          |
|   1 |  SORT AGGREGATE    |      |     1 |     5 |            |          |
|*  2 |   TABLE ACCESS FULL| XXY  |  1000K|  4882K|   429   (2)| 00:00:01 |
---------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   2 - filter(DECODE(TO_CHAR(SYS_OP_VECBIT("SYS_NC00002$",0)),NULL,NVL("
              B",1),'0',NVL("B",1),'1',"B")=1)
Note
-----
   - statistics feedback used for this statement

Le plan d'exécution sur 12c montre par opposition à 11g une partie prédicat complexe impliquant une nouvelle colonne interne SYS_NC00006$ .

Ce prédicat indique qu'en interne, Oracle considère toujours que la colonne B peut potentiellement contenir des valeurs non par défaut. Cela signifie - Oracle au début ne met pas physiquement à jour chaque ligne avec la valeur par défaut.

Pourquoi une nouvelle colonne interne SYS_NC00006$ est créé?

12c> select column_name, virtual_column, hidden_column, user_generated 
from user_tab_cols
where table_name = 'XXY'
;
COLUMN_NAME      VIR HID USE
---------------- --- --- ---
B                NO  NO  YES
SYS_NC00002$     NO  YES NO 
A                NO  NO  YES

12c> select a, b, SYS_NC00002$ hid from xxy where a in (1,10);

        A          B HID            
---------- ---------- ----------------
         1          1                 
        10          1                 

12c> update xxy set b=1 where a=10 and b=1;
1 row updated.

12c> select a, b, SYS_NC00002$ hid from xxy where a in (1,10);
         A          B HID            
---------- ---------- ----------------
         1          1                 
        10          1 01              

Notez la différence entre les valeurs de B et les colonnes internes associées. Oracle vérifie simplement via sa colonne interne générée par le système (par exemple, SYS_NC00006$ ) et via le SYS_OP_VECBIT fonction s'il faut considérer la valeur par défaut de la colonne B ou la valeur réelle modifiée via une instruction DML explicite.

Qu'est-ce qu'il y a avec deux instructions alter distinctes ?

12c> alter table xxy add (b integer);
12c> alter table xxy modify b default 1;

12c> select count(b), count(coalesce(b,0)) nulls  from xxy where b = 1 or b is null;

  COUNT(B)      NULLS
---------- ----------
         0    1000000

La valeur de la nouvelle colonne reste NULL pour toutes les lignes. Aucune mise à jour réelle n'est nécessaire, par conséquent, l'instruction DDL ne sera pas optimisée.

ici est un article OTN qui explique plus en détail la nouvelle optimisation DDL.