<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Oracle - Concepts et Exemples &#187; 12c</title>
	<atom:link href="https://blog.developpez.com/pachot/category/microlearning/12c/feed/" rel="self" type="application/rss+xml" />
	<link>https://blog.developpez.com/pachot</link>
	<description>Les fonctionalités et concepts d&#039;Oracle à partir de traductions et de démos</description>
	<lastBuildDate>Sun, 03 Apr 2016 20:36:21 +0000</lastBuildDate>
	<language>fr-FR</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>https://wordpress.org/?v=4.1.42</generator>
	<item>
		<title>Mécanismes internes du multitenant: liens objects et métadonnées</title>
		<link>https://blog.developpez.com/pachot/multitenant-metadata-object-link/</link>
		<comments>https://blog.developpez.com/pachot/multitenant-metadata-object-link/#comments</comments>
		<pubDate>Fri, 03 Oct 2014 14:07:43 +0000</pubDate>
		<dc:creator><![CDATA[pachot]]></dc:creator>
				<category><![CDATA[12c]]></category>
		<category><![CDATA[MicroLearning]]></category>

		<guid isPermaLink="false">http://blog.developpez.com/pachot/?p=1216</guid>
		<description><![CDATA[ceci est la traduction de mon article sur le blog dbi-services Attention: il ne s&#8217;agit ici que de jouer avec les mécanismes internes non documentés. A ne faire que sur une base de test prête à être jetée. Jouer avec &#8230; <a href="https://blog.developpez.com/pachot/multitenant-metadata-object-link/">Lire la suite <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<blockquote><p>ceci est la traduction de mon article sur le blog <a href="http://www.dbi-services.com/index.php/blog/entry/oracle-12c-cdb-metadata-a-object-links-internals" title="Oracle 12c CDB - metadata &amp; object links internals">dbi-services</a></p></blockquote>
<p>Attention: il ne s&rsquo;agit ici que de jouer avec les mécanismes internes non documentés. A ne faire que sur une base de test prête à être jetée. Jouer avec ces mécanisme est le meilleur moyen de planter la base avec des ORA-600 et de corrompre le dictionnaire.</p>
<p>Sur une base multitenant (CDB) nous savons que chaque pluggable database est isolée afin d&rsquo;être vue comme une seule base lorsqu&rsquo;on est connecté à son service. Mais elles partagent des ressource communes puisque le but est de consolider plusieurs bases en une seule. Pas de problème pour la CPU, la mémoire, les journaux redo et undo: il ne sont gérés qu&rsquo;au niveau CDB. Le multitenant n&rsquo;a pas changé grand chose là dessus, à part qu&rsquo;on stocke l&rsquo;identificateur de conteneur dans différentes structures, par exemple dans les vecteurs de redo pour identifier les objets (object id peuvent être les mêmes entre plusieurs CDB). Au niveau des fichiers, pas de gros changements par rapport à ce qui avait été introduit avec les tablespaces transportables.</p>
<p>Mais ce qui a été un plus gros challenge pour les développeurs d&rsquo;Oracle 12c, c&rsquo;est surtout la manière d&rsquo;implémenter le dictionnaire. Il a été séparé:</p>
<ul>
<li>Tout ce qui est interne au fonctionnement d&rsquo;Oracle, les métadonnées du dictionnaire, les packages dbms_&#8230;, tout ce qui est stocké pour toute l&rsquo;instance: tables de références, repository AWR, etc. C&rsquo;est le but: consolider, ne pas répéter plusieurs fois ce qui est identique du moment qu&rsquo;on est sur la même version d&rsquo;Oracle</li>
<li>Toutes les métadonnées qui concernent l&rsquo;application: les tables et packages que vous avez créé. Celles-ci sont propres à chaque pluggable tablespace, et sont transportées, clonées avec les oprérations de plug/unplug</li>
</ul>
<p>et finalement, comme le but principal d&rsquo;Oracle étant de pouvoir faire dans une PDB tout ce qu&rsquo;on peut faire sur base, il faut pouvoir foir au niveau PDB les informations stockées au niveau CDB.</p>
<p>Tout ça semble un peu magique par rapport à ce qu&rsquo;on connait d&rsquo;Oracle qui n&rsquo;a jamais fait cette séparation. Par exemple depuis le début d&rsquo;Oracle on a dans la table SYS.TAB$ les informations de nos tables, ainsi que les informations des tables du dictionnaire &#8211; incluant les métadonnées de la table SYS.TAB$ elle-même. La documentation ne va pas très loin sur la manière dont ça a été implémenté. Heureusement on a quelques scripts dans ?/rdbms/admin qui donnes des idées là dessus. ils utilisent une syntaxe un peu particulière, non documentée, et qui n&rsquo;est disponible que lorsque les scripts internes mettent le paramètre non documenté &laquo;&nbsp;_ORACLE_SCRIPT&nbsp;&raquo;=true. </p>
<p>Maintenant, ce sont des trucs de geek qui arrivent. Rien à voir avec ce qu^&rsquo;on fait en production. Nous allons créer nos propres liens objet et métadonnée. Ce qui est fait dans la suite est fait dans une session avec &laquo;&nbsp;_ORACLE_SCRIPT&nbsp;&raquo;=true. On va voir des nouveaux éléments de syntaxe:  cdb$view(), sharing=metadata, sharing=object, common_data</p>
<h2>Container data objects</h2>
<p>D&rsquo;abord nous allons voir comment au niveau CDB la racine CDB$ROOT peut voir les informations des autres conteneurs (PDB)</p>
<p>Je suis au niveau CDB:</p>
<pre>SQL&gt; alter session set container=cdb$root;
Session altered.

SQL&gt; show con_name
CON_NAME
------------------------------
CDB$ROOT

SQL&gt; show con_id
CON_ID
------------------------------
1</pre>
<p>et je créé une table normale:</p>
<pre>
SQL&gt; create table DEMO_REG_TABLE sharing=none as select 111 dummy from dual;
Table created.

SQL&gt; select * from DEMO_REG_TABLE;
     DUMMY
----------
       111
</pre>
<p>Puis je fais exactement la même chose dans la PDB:</p>
<pre>
SQL&gt; alter session set container=pdb1;
Session altered.

SQL&gt; show con_name
CON_NAME
------------------------------
PDB1

SQL&gt; show con_id
CON_ID
------------------------------
3

SQL&gt; create table DEMO_REG_TABLE sharing=none as select 999 dummy from dual;
Table created.

SQL&gt; select * from DEMO_REG_TABLE;
     DUMMY
----------
       999
</pre>
<p>et de retour dans la racine, j&rsquo;utilse la fonction de table CDB$VIEW pour voir ce que j&rsquo;ai dans chacune de mes PDB:</p>
<pre>
SQL&gt; select * from  cdb$view(DEMO_REG_TABLE) where con_id in (1,3);
     DUMMY     CON_ID
---------- ----------
       999          3
       111          1
</pre>
<p>C&rsquo;est comme ça que les &lsquo;container objects&rsquo; sont définis: ils utilisent la fonction CDB$VIEW pour exécuter une requête sur chaque PDB, consolider le résultat et rajouter l&rsquo;identifiant de PDB: CON_ID pour montrer d&rsquo;où viennent les lignes.</p>
<p>Vous voulez savoir comment c&rsquo;est implémenté? Il semble que ce soit le mécanisme de parallel query qui soit utilisé. Voici la preuve: </p>
<pre>
SQL&gt; select * from  cdb$view(DEMO_REG_TABLE);
select * from  cdb$view(DEMO_REG_TABLE)
*
ERROR at line 1:
ORA-12801: error signaled in parallel query server P002
ORA-00942: table or view does not exist
</pre>
<p>Lorsque la table n&rsquo;est pas trouvée (j&rsquo;ai volontairement créé une table sur quelques conteneurs seulement) c&rsquo;est un process parallel query qui renvoit l&rsquo;erreur.</p>
<h2>Metadata links</h2>
<p>Maintenant, je vais créer une fonction PL/SQ dans la racine et dans une PDB, mais je n&rsquo;ai pas envie de dupliquer le code dans les deux tablespaces SYSTEM. Je vais utiliser SHARING=METADATA pour créer un metadata link.</p>
<pre>
SQL&gt; alter session set container=cdb$root;
Session altered.

SQL&gt; show con_name
CON_NAME
------------------------------
CDB$ROOT

SQL&gt; show con_id

CON_ID
------------------------------
1

SQL&gt; create function DEMO_MDL_FUNCTION sharing=metadata
  2  return varchar2 as dummy varchar2(100); begin select max(dummy) into dummy from DEMO_REG_TABLE; return dummy; end;
  3  /
Function created.

SQL&gt; select DEMO_MDL_FUNCTION from dual;
DEMO_MDL_FUNCTION
------------------------------
111
</pre>
<pre>
SQL&gt; alter session set container=pdb1;
Session altered.

SQL&gt; show con_name
CON_NAME
------------------------------
PDB1

SQL&gt; show con_id
CON_ID
------------------------------
3

SQL&gt; create function DEMO_MDL_FUNCTION sharing=metadata
  2  return varchar2 as dummy varchar2(100); begin select max(dummy) into dummy from DEMO_REG_TABLE; return dummy; end;
  3  /
Function created.

SQL&gt; select DEMO_MDL_FUNCTION from dual;
DEMO_MDL_FUNCTION
------------------------------
999
</pre>
<p>Je peux donc voir le code dans SYS.SOURCE$ de la racine:</p>
<pre>
SQL&gt; alter session set container=cdb$root;
Session altered.

SQL&gt; select * from source$ where obj# in (select obj# from obj$ where name like 'DEMO%');

      OBJ#       LINE SOURCE
---------- ---------- ------------------------------
     95789          1 function DEMO_MDL_FUNCTION
</pre>
<p>Mais rien dans ma PDB:</p>
<pre> 
SQL&gt; alter session set container=pdb1;
Session altered.

SQL&gt; select * from source$ where obj# in (select obj# from obj$ where name like 'DEMO%');

no rows selected
</pre>
<h2>Object links</h2>
<p>Ceci était pour ne stocker les métadonnées qu&rsquo;à un seul endroit, dans la racine, en ayant seulement un lien dessus dans les PDB. On peut aussi faire la même chose pour des données qu&rsquo;on ne veut stocker que dans la racine, en utilisant SHARING=METADATA pour créer la table et SHARING=OBJECT pour créer une vue dessus</p>
<pre>
SQL&gt; alter session set container=cdb$root;
Session altered.

SQL&gt; show con_name
CON_NAME
------------------------------
CDB$ROOT

SQL&gt; show con_id
CON_ID
------------------------------
1

SQL&gt; create table DEMO_MDL_TABLE sharing=metadata as select 111 dummy from dual;
Table created.

SQL&gt; alter session set container=pdb1;
Session altered.

SQL&gt; show con_name
CON_NAME
------------------------------
PDB1

SQL&gt; show con_id

CON_ID
------------------------------
3

SQL&gt; create table DEMO_MDL_TABLE sharing=metadata as select 999 dummy  from dual;
Table created.
</pre>
<pre> 
SQL&gt; alter session set container=cdb$root;
Session altered.

SQL&gt; select * from  cdb$view(DEMO_MDL_TABLE) where con_id in (1,3);

     DUMMY     CON_ID
---------- ----------
       999          3
       111          1
</pre>
<pre>
SQL&gt; alter session set container=cdb$root;
Session altered.

SQL&gt; show con_name
CON_NAME
------------------------------
CDB$ROOT

SQL&gt; show con_id
CON_ID
------------------------------
1

SQL&gt; create view DEMO_OBL_VIEW sharing=object as select * from DEMO_MDL_TABLE;
View created.

SQL&gt; select * from DEMO_OBL_VIEW;
     DUMMY
----------
       111
</pre>
<pre> 
SQL&gt; alter session set container=pdb1;
Session altered.

SQL&gt; show con_name
CON_NAME
------------------------------
PDB1

SQL&gt; show con_id
CON_ID
------------------------------
3

SQL&gt; create view DEMO_OBL_VIEW sharing=object as select * from DEMO_MDL_TABLE;
View created.

SQL&gt; select * from DEMO_OBL_VIEW;
     DUMMY
----------
       111
</pre>
<p>La vue montre les données stockées dans CDB$ROOT en suivant le &lsquo;object link&rsquo; au lieu de lire la table du container courant.</p>
<p>On ne peut pas insérer directement dans un &lsquo;object link':</p>
<pre>
SQL&gt; insert into DEMO_OBL_VIEW select 9999 dummy from dual;
insert into DEMO_OBL_VIEW select 9999 dummy from dual
            *
ERROR at line 1:
ORA-02030: can only select from fixed tables/views
</pre>
<p>et on a une idée de l&rsquo;implémentation en regardant le plan d&rsquo;exécution:</p>
<pre>
---------------------------------------------
| Id  | Operation        | Name             |
---------------------------------------------
|   0 | SELECT STATEMENT |                  |
|   1 |  FIXED TABLE FULL| X$OBLNK$aed0818c |
---------------------------------------------
</pre>
<p>C&rsquo;est une table X$ qui va faire le lien </p>
<h2>Common data views</h2>
<p>finalement, comment une PDB peut voir les données de la racine? Nous avons vu au dessus que la fonction que j&rsquo;ai créé ci-dessus est stockée dans le SYS.SOURCE$ de la racine seulement. Par contre on peut la voir à partir du DBA_SOURCE de la PDB</p>
<pre>
SQL&gt; alter session set container=cdb$root;
Session altered.

SQL&gt; show con_name
CON_NAME
------------------------------
CDB$ROOT

SQL&gt; show con_id
CON_ID
------------------------------
1

SQL&gt; create or replace view DEMO_INT_VIEW common_data (dummy,sharing) as select dummy,case when dummy='222' then 0 else 1 end from DEMO_MDL_TABLE;
View created.

SQL&gt; select * from DEMO_INT_VIEW;

     DUMMY    SHARING
---------- ----------
       111          1
       222          0
</pre>
<p>J&rsquo;ai créé une &lsquo;common data view&rsquo; en rajoutant une colonne &lsquo;sharing&rsquo; pour montrer d&rsquo;où viennent les lignes.</p>
<pre>
SQL&gt; alter session set container=pdb1;
Session altered.

SQL&gt; show con_name
CON_NAME
------------------------------
PDB1

SQL&gt; show con_id
CON_ID
------------------------------
3

SQL&gt; create or replace view DEMO_INT_VIEW common_data (dummy,sharing) as select dummy,case when dummy='222' then 0 else 1 end from DEMO_MDL_TABLE;
View created.

SQL&gt; select * from DEMO_INT_VIEW;
     DUMMY    SHARING ORIGIN_CON_ID
---------- ---------- -------------
       999          1             3
       111          1             1
</pre>
<pre>
SQL&gt; set autotrace on
SQL&gt; select * from DEMO_INT_VIEW;

     DUMMY    SHARING ORIGIN_CON_ID
---------- ---------- -------------
       111          1             1
       999          1             3

Execution Plan
----------------------------------------------------------
Plan hash value: 3158883863

--------------------------------------------------------------------------------------------
|Id  | Operation               | Name            |Pstart|Pstop |   TQ  |IN-OUT| PQ Distrib |
--------------------------------------------------------------------------------------------
|  0 | SELECT STATEMENT        |                 |      |      |       |      |            |
|  1 |  PX COORDINATOR         |                 |      |      |       |      |            |
|  2 |   PX SEND QC (RANDOM)   | :TQ10000        |      |      | Q1,00 | P-&gt;S | QC (RAND)  |
|  3 |    PX PARTITION LIST ALL|                 |    1 |    2 | Q1,00 | PCWC |            |
|  4 |     FIXED TABLE FULL    | X$COMVW$e40eb386|      |      | Q1,00 | PCWP |            |
--------------------------------------------------------------------------------------------
</pre>
<p>On voit que les données de chaque conteneur est retourné comme une partition.</p>
]]></content:encoded>
			<wfw:commentRss></wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>12c: les niveaux de compression changent de nom</title>
		<link>https://blog.developpez.com/pachot/compression_ratio/</link>
		<comments>https://blog.developpez.com/pachot/compression_ratio/#comments</comments>
		<pubDate>Tue, 12 Nov 2013 15:27:57 +0000</pubDate>
		<dc:creator><![CDATA[pachot]]></dc:creator>
				<category><![CDATA[12c]]></category>
		<category><![CDATA[compression]]></category>
		<category><![CDATA[HCC]]></category>

		<guid isPermaLink="false">http://blog.developpez.com/pachot/?p=767</guid>
		<description><![CDATA[En 12c les niveaux de compression changent de noms, en présisant le stockage en ligne ou colonne en ligne (ROW STORE): les colonnes d&#8217;une même ligne sont ensembles, les lignes sont stockées les unes à la suite des autres dans &#8230; <a href="https://blog.developpez.com/pachot/compression_ratio/">Lire la suite <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<p>En 12c les niveaux de compression changent de noms, en présisant le stockage en ligne ou colonne</p>
<ul>
<li>en ligne (ROW STORE): les colonnes d&rsquo;une même ligne sont ensembles, les lignes sont stockées les unes à la suite des autres dans des blocs
</li>
<li>ou en colonne (COLUMN STORE): les valeurs des colonnes de plusieurs lignes sont regroupées en vecteurs, Les vecteurs sont stockés dans le Compression Unit<br />
Si vous avez déjà utilisé des <a href="http://docs.oracle.com/cd/E16655_01/appdev.121/e18410/ch_nine.htm#ZZPRE846" title="Host Array">Host Array</a> pour insérer en bulk, c&rsquo;est un peu la même idée. On a une meilleur compression lorsqu&rsquo;on retrouve les mêmes valeurs d&rsquo;une ligne à l&rsquo;autre.
</li>
</ul>
<p><strong>Sans option Advanced compression:</strong></p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">NOCOMPRESS</div></div>
<p>Pas de compression</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">ROW STORE COMPRESS BASIC</div></div>
<p>Basic Table Compression (aka &lsquo;COMPRESS&rsquo; ou &lsquo;ROW STORE COMPRESS&rsquo;)<br />
Lors des insert en direct-path, au niveau de chaque bloc, les valeurs dupliquées ne sont stockées qu&rsquo;une fois (en utilisant des pointeurs &#8211; utile seulement pour des tailles supérieures à quelques octets)<br />
Lors de DML conventionnel, les lignes sont décompressées, donc utile en chargement de masse seulement.</p>
<p><strong>Avec option Advanced Compression</strong></p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">ROW STORE COMPRESS ADVANCED</div></div>
<p>Advanced Table Compression (aka &lsquo;COMPRESS FOR OLTP&rsquo;)<br />
Même chose que BASIC, mais le DML conventionnel permet de compresser aussi.</p>
<p><strong>sous Exadata et ZFS, avec stockage colonne</strong></p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">COLUMN STORE COMPRESS FOR QUERY LOW/HIGH</div></div>
<p>Warehouse compression (aka Hybrid Columnar Compression QUERY LOW/HIGH)<br />
Stockage en colonne dans des Compression Unit, utilise plus de CPU (surtout en HIGH)<br />
Lors des inserts en direct-path seulement, le DML conventionnel remet les lignes en stockage ligne, donc avec moins bon taux de compression et chained rows.<br />
QUERY: Valable pour des données qui ne sont pas modifiées, mais la lecture est rapide (l&rsquo;économie d&rsquo;I/O compense le surplus de CPU)</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">COLUMN STORE COMPRESS FOR ARCHIVE LOW/HIGH</div></div>
<p>Archive compression (aka Hybrid Columnar Compression ARCHIVE LOW/HIGH)<br />
Stockage en colonne dans des Compression Unit, utilise plus de CPU (surtout en HIGH)<br />
Lors des inserts en direct-path seulement, le DML conventionnel remet les lignes en stockage ligne, donc avec moins bon taux de compression et chained rows.<br />
ARCHIVE: Valable pour des données qui ne sont pas modifiées et peu lues (car forte utilisation CPU pour décompresser aussi)</p>
]]></content:encoded>
			<wfw:commentRss></wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>12c: des noms de Wait Events plus parlants</title>
		<link>https://blog.developpez.com/pachot/12c_display_name/</link>
		<comments>https://blog.developpez.com/pachot/12c_display_name/#comments</comments>
		<pubDate>Mon, 28 Oct 2013 17:05:34 +0000</pubDate>
		<dc:creator><![CDATA[pachot]]></dc:creator>
				<category><![CDATA[12c]]></category>
		<category><![CDATA[db file sequential read]]></category>
		<category><![CDATA[db file squattered read]]></category>
		<category><![CDATA[wait events]]></category>

		<guid isPermaLink="false">http://blog.developpez.com/pachot/?p=871</guid>
		<description><![CDATA[Certains noms de wait events sont trompeurs. C&#8217;est le cas par exemple de &#8216;db file sequential read&#8217; qui n&#8217;est pas ce qu&#8217;on entend par lecture disque séquentielle, mais plutôt une lecture monobloc (donc plutôt des i/o randoms). Alors que &#8216;log &#8230; <a href="https://blog.developpez.com/pachot/12c_display_name/">Lire la suite <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<p>Certains noms de wait events sont trompeurs. C&rsquo;est le cas par exemple de &lsquo;db file sequential read&rsquo; qui n&rsquo;est pas ce qu&rsquo;on entend par lecture disque séquentielle, mais plutôt une lecture monobloc (donc plutôt des i/o randoms).<br />
Alors que &lsquo;log file sequential read&rsquo; est bien une lecture de plusieurs blocs contigus.</p>
<p>La <a href="http://docs.oracle.com/cd/E16655_01/server.121/e17615/waitevents003.htm#BGGIBDJI" title="doc">doc</a> précise leur définition, mais en 12c on a aussi un &lsquo;Display Name&rsquo; qui permet à Oracle de montrer un nom un peu plus parlant.</p>
<p>Voici ceux dont le &lsquo;display name&rsquo; est différent du nom de l&rsquo;event:</p>
<pre>select wait_class,name, display_name from v$event_name where display_name != name order by 1,2;</pre>
<table>
<tr>
<th scope="col">WAIT_CLASS
</th>
<th scope="col">NAME
</th>
<th scope="col">DISPLAY_NAME
</th>
</tr>
<tr>
<td>Administrative
</td>
<td>concurrent I/O completion
</td>
<td>online move datafile IO completion
</td>
</tr>
<tr>
<td>Administrative
</td>
<td>datafile copy range completion
</td>
<td>online move datafile copy range completion
</td>
</tr>
<tr>
<td>Administrative
</td>
<td>wait for possible quiesce finish
</td>
<td>quiesce database completion
</td>
</tr>
<tr>
<td>Commit
</td>
<td>log file sync
</td>
<td>commit: log file sync
</td>
</tr>
<tr>
<td>Configuration
</td>
<td>log buffer space
</td>
<td>log buffer full &#8211; LGWR bottleneck
</td>
</tr>
<tr>
<td>Idle
</td>
<td>LGWR real time apply sync
</td>
<td>standby apply advance notification
</td>
</tr>
<tr>
<td>Other
</td>
<td>DFS db file lock
</td>
<td>quiesce for datafile offline
</td>
</tr>
<tr>
<td>Other
</td>
<td>Image redo gen delay
</td>
<td>redo resource management
</td>
</tr>
<tr>
<td>Other
</td>
<td>datafile move cleanup during resize
</td>
<td>online move datafile resize cleanup
</td>
</tr>
<tr>
<td>System I/O
</td>
<td>control file sequential read
</td>
<td>control file read
</td>
</tr>
<tr>
<td>System I/O
</td>
<td>control file single write
</td>
<td>control file write
</td>
</tr>
<tr>
<td>System I/O
</td>
<td>db file parallel write
</td>
<td>db list of blocks write
</td>
</tr>
<tr>
<td>System I/O
</td>
<td>log file parallel write
</td>
<td>log file redo write
</td>
</tr>
<tr>
<td>System I/O
</td>
<td><strong>log file sequential read</strong>
</td>
<td><strong>log file multiblock read</strong>
</td>
</tr>
<tr>
<td>System I/O
</td>
<td>log file single write
</td>
<td>log file header write
</td>
</tr>
<tr>
<td>User I/O
</td>
<td>db file parallel read
</td>
<td>db list of blocks read
</td>
</tr>
<tr>
<td>User I/O
</td>
<td><strong>db file scattered read</strong>
</td>
<td><strong>db multiblock read</strong>
</td>
</tr>
<tr>
<td>User I/O
</td>
<td><strong>db file sequential read</strong>
</td>
<td><strong>db single block read</strong>
</td>
</tr>
<tr>
<td>User I/O
</td>
<td>db file single write
</td>
<td>db single block write
</td>
</tr>
</table>
<p>La différentiation des i/o single block et multiblock est particulièrement intéressante.</p>
]]></content:encoded>
			<wfw:commentRss></wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>12c: Parallel Query &#8216;replicate&#8217; au lieu de &#8216;broadcast&#8217; pour les petites tables</title>
		<link>https://blog.developpez.com/pachot/12c_pq_replicate/</link>
		<comments>https://blog.developpez.com/pachot/12c_pq_replicate/#comments</comments>
		<pubDate>Mon, 14 Oct 2013 20:50:21 +0000</pubDate>
		<dc:creator><![CDATA[pachot]]></dc:creator>
				<category><![CDATA[12c]]></category>
		<category><![CDATA[broadcast]]></category>
		<category><![CDATA[parallel query]]></category>
		<category><![CDATA[pq_replicate]]></category>

		<guid isPermaLink="false">http://blog.developpez.com/pachot/?p=829</guid>
		<description><![CDATA[Sur un Hash Join en parallel query, lorsque la table hachée est petite, il est préférable parfois de l&#8217;envoyer entière à chacun des process parallèle qui effectuent la jointure. En 12c il y a le Hybrid Hash qui va décider &#8230; <a href="https://blog.developpez.com/pachot/12c_pq_replicate/">Lire la suite <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<p>Sur un Hash Join en parallel query, lorsque la table hachée est petite, il est préférable parfois de l&rsquo;envoyer entière à chacun des process parallèle qui effectuent la jointure.<br />
En 12c il y a le Hybrid Hash qui va décider du broadcast au moment de l&rsquo;exécution si la table petite.<br />
Mais si le plan force un broadcast, il y a une nouvelle distribution possible en 12c: PQ_REPLICATE</p>
<p>Voici un plan d&rsquo;exécution en 11g avec un DOP 8 avec les outlines suivants:</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">&nbsp;parallel(dept) parallel(emp) leading(dept emp) use_hash(emp) no_swap_join_inputs(emp) pq_distribute(emp broadcast none)</div></div>
<pre>
-------------------------------------------------------------------------------------------------------------------------------------------------------------------
| Id  | Operation               | Name     | Starts | E-Rows | Cost (%CPU)|    TQ  |IN-OUT| PQ Distrib | A-Rows |   A-Time   | Buffers |  OMem |  1Mem |  O/1/M   |
-------------------------------------------------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT        |          |      1 |        |     4 (100)|        |      |            |     14 |00:00:00.13 |       8 |       |       |          |
|   1 |  PX COORDINATOR         |          |      1 |        |            |        |      |            |     14 |00:00:00.13 |       8 |       |       |          |
|   2 |   PX SEND QC (RANDOM)   | :TQ10001 |      0 |     14 |     4   (0)|  Q1,01 | P-&gt;S | QC (RAND)  |      0 |00:00:00.01 |       0 |       |       |          |
|*  3 |    HASH JOIN            |          |      8 |     14 |     4   (0)|  Q1,01 | PCWP |            |     14 |00:00:00.08 |      56 |   684K|   684K|     8/0/0|
|   4 |     PX RECEIVE          |          |      8 |      4 |     2   (0)|  Q1,01 | PCWP |            |     32 |00:00:00.06 |       0 |       |       |          |
|   5 |      PX SEND BROADCAST  | :TQ10000 |      0 |      4 |     2   (0)|  Q1,00 | P-&gt;P | BROADCAST  |      0 |00:00:00.01 |       0 |       |       |          |
|   6 |       PX BLOCK ITERATOR |          |      8 |      4 |     2   (0)|  Q1,00 | PCWC |            |      4 |00:00:00.01 |      12 |       |       |          |
|*  7 |        TABLE ACCESS FULL| DEPT     |      4 |      4 |     2   (0)|  Q1,00 | PCWP |            |      4 |00:00:00.01 |      12 |       |       |          |
|   8 |     PX BLOCK ITERATOR   |          |      8 |     14 |     2   (0)|  Q1,01 | PCWC |            |     14 |00:00:00.02 |      56 |       |       |          |
|*  9 |      TABLE ACCESS FULL  | EMP      |     14 |     14 |     2   (0)|  Q1,01 | PCWP |            |     14 |00:00:00.01 |      56 |       |       |          |
-------------------------------------------------------------------------------------------------------------------------------------------------------------------
</pre>
<p>Ici DEPT (4 lignes) est lue en parallèle par les 8 process parallèles du set 1 (Q1,00) et toutes les lignes sont envoyées au set 2 (Q1,01) pour la jointure. On le voit aux 4&#215;8=32 lignes reçues dans A-Rows.</p>
<p>En 12c, &lsquo;PQ replicate small tables&rsquo; va économiser un set de serveurs parallèles en faisant lire l&rsquo;ensemble de la table DEPT par chacun des process qui fait la jointure:</p>
<pre>
-----------------------------------------------------------------------------------------------------------------------------------------------------------------
| Id  | Operation             | Name     | Starts | E-Rows | Cost (%CPU)|    TQ  |IN-OUT| PQ Distrib | A-Rows |   A-Time   | Buffers |  OMem |  1Mem |  O/1/M   |
-----------------------------------------------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT      |          |      1 |        |     4 (100)|        |      |            |     14 |00:00:00.11 |       5 |       |       |          |
|   1 |  PX COORDINATOR       |          |      1 |        |            |        |      |            |     14 |00:00:00.11 |       5 |       |       |          |
|   2 |   PX SEND QC (RANDOM) | :TQ10000 |      0 |     14 |     4   (0)|  Q1,00 | P-&gt;S | QC (RAND)  |      0 |00:00:00.01 |       0 |       |       |          |
|*  3 |    HASH JOIN          |          |      8 |     14 |     4   (0)|  Q1,00 | PCWP |            |     14 |00:00:00.02 |     104 |   684K|   684K|     8/0/0|
|   4 |     TABLE ACCESS FULL | DEPT     |      8 |      4 |     2   (0)|  Q1,00 | PCWP |            |     32 |00:00:00.01 |      48 |       |       |          |
|   5 |     PX BLOCK ITERATOR |          |      8 |     14 |     2   (0)|  Q1,00 | PCWC |            |     14 |00:00:00.01 |      56 |       |       |          |
|*  6 |      TABLE ACCESS FULL| EMP      |     14 |     14 |     2   (0)|  Q1,00 | PCWP |            |     14 |00:00:00.01 |      56 |       |       |          |
-----------------------------------------------------------------------------------------------------------------------------------------------------------------
</pre>
<p>Maintenant, DEPT est lue en entier (pas de block iterator) par chacun des process. On a un seul set de process parallèle (Q1,00) qui fait tout.</p>
<p>Ca fait un peu plus de blocs à lire, mais une économie de process et de messages parallel query. Efficace lorsque on sait que la table est petite. Et c&rsquo;est en principe le cas si on choisit une distribution broadcast.</p>
<p><ins datetime="2013-10-14T20:39:16+00:00">La <a href="http://ora-demo.pachot.net/12c_pq_replicate.html" title="demo" target="_blank">demo</a> montre aussi un plan possible en 11g</ins> où DEPT n&rsquo;est pas lue par les process parallèle, mais est broadcastée quand même.</p>
<p>Le hint pour contrôler cette fonctionnalité: PQ_REPLICATE. A noter que l&rsquo;on n&rsquo;a plus de Adaptive Parallel Query Distribution (HYBRID HASH) dans ce cas.</p>
]]></content:encoded>
			<wfw:commentRss></wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>12c: la transformation Partial Join Evaluation</title>
		<link>https://blog.developpez.com/pachot/12c_partial_join_evaluation/</link>
		<comments>https://blog.developpez.com/pachot/12c_partial_join_evaluation/#comments</comments>
		<pubDate>Tue, 01 Oct 2013 19:43:32 +0000</pubDate>
		<dc:creator><![CDATA[pachot]]></dc:creator>
				<category><![CDATA[12c]]></category>
		<category><![CDATA[Hash Join Semi]]></category>
		<category><![CDATA[Partial join evaluation]]></category>
		<category><![CDATA[PJE]]></category>
		<category><![CDATA[Transformation]]></category>

		<guid isPermaLink="false">http://blog.developpez.com/pachot/?p=800</guid>
		<description><![CDATA[Faut-il remplacer les jointures par des EXISTS lorsque c&#8217;est possible ? Une sous-requête EXISTS n&#8217;a pas besoin de ramener toutes les lignes d&#8217;une jointure: dès qu&#8217;il y a une correspondance pour une valeur de la jointure, on peut passer à &#8230; <a href="https://blog.developpez.com/pachot/12c_partial_join_evaluation/">Lire la suite <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<p>Faut-il remplacer les jointures par des EXISTS lorsque c&rsquo;est possible ?<br />
Une sous-requête EXISTS n&rsquo;a pas besoin de ramener toutes les lignes d&rsquo;une jointure: dès qu&rsquo;il y a une correspondance pour une valeur de la jointure, on peut passer à la suivante. C&rsquo;est un Semi Hash Join, qui est plus rapide qu&rsquo;un Hash Join. Très souvent le CBO va faire cette réécriture lors des transformations de la phase d&rsquo;optimisation.<br />
En en 12c on a une transformation automatique de plus lorsque les lignes de la jointure seront au final dédoublonnées avec un DISTINCT.</p>
<p>Par exemple, la requête suivante:</p>
<pre>select distinct status_label from TEST_STATUS 
 join TEST using(status_id) where flag='Y';</pre>
<p>sera transformée en:</p>
<pre>select status_label from TEST_STATUS 
where exists(
 select null from TEST 
 where flag='Y' and TEST.status_id=TEST_STATUS.status_id
);</pre>
<p>vu qu&rsquo;on ne renvoit que les colonnes de TEST_STATUS, un EXISTS (ou IN) est suffisant. Et on obtient le plan d&rsquo;exécution suivant:</p>
<pre>------------------------------------------------------------------------------------------------------------------------
| Id  | Operation           | Name        | Starts | E-Rows | A-Rows |   A-Time   | Buffers |  OMem |  1Mem | Used-Mem |
------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT    |             |      1 |        |     10 |00:00:00.01 |       7 |       |       |          |
|   1 |  HASH UNIQUE        |             |      1 |     10 |     10 |00:00:00.01 |       7 |  1214K|  1214K| 1105K (0)|
|*  2 |   HASH JOIN SEMI    |             |      1 |     10 |     10 |00:00:00.01 |       7 |  1185K|  1185K| 1142K (0)|
|   3 |    TABLE ACCESS FULL| TEST_STATUS |      1 |     10 |     10 |00:00:00.01 |       3 |       |       |          |
|*  4 |    TABLE ACCESS FULL| TEST        |      1 |  50000 |     10 |00:00:00.01 |       4 |       |       |          |
------------------------------------------------------------------------------------------------------------------------</pre>
<p>Ici on voit le gros avantage dans la colonne A-Rows: le Full Table Scan de la table TEST qui fait 100000 lignes, s&rsquo;est arrêté dès qu&rsquo;on a trouvé les 10 correspondances avec la table TEST_STATUS. Pas besoin de continuer: le prédicat EXISTS est vérifié.<br />
C&rsquo;est le HASH JOIN SEMI qui fait ça alors qu&rsquo;un HASH JOIN aurait lu 100000 lignes et renvoyé 50000 lignes jusqu&rsquo;au DISTINCT. </p>
<p>Voici le plan en désactivant les transformations:</p>
<pre>------------------------------------------------------------------------------------------------------------------------
| Id  | Operation           | Name        | Starts | E-Rows | A-Rows |   A-Time   | Buffers |  OMem |  1Mem | Used-Mem |
------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT    |             |      1 |        |     10 |00:00:00.09 |     214 |       |       |          |
|   1 |  HASH UNIQUE        |             |      1 |     10 |     10 |00:00:00.09 |     214 |  1214K|  1214K| 1099K (0)|
|*  2 |   HASH JOIN         |             |      1 |  50000 |    100K|00:00:00.07 |     214 |  1185K|  1185K| 1129K (0)|
|   3 |    TABLE ACCESS FULL| TEST_STATUS |      1 |     10 |     10 |00:00:00.01 |       3 |       |       |          |
|*  4 |    TABLE ACCESS FULL| TEST        |      1 |  50000 |    100K|00:00:00.01 |     211 |       |       |          |
------------------------------------------------------------------------------------------------------------------------</pre>
<p>A noter que en 11.2 il y avait déjà une transformation utile dans ce cas, Distinct Placement qui fait descendre le DISTINCT sous la jointure, mais qui nécessite quand même de lire toute la table.</p>
<p><ins datetime="2013-10-01T18:59:10+00:00">Demo, hints, plans d&rsquo;exécution et traces 10053 complétes dans la <a href="http://ora-demo.pachot.net/12c_partial_join_evaluation.html" title="demo" target="_blank">demo</a></ins></p>
<p>Alors pourquoi faire ça plutôt que de réécrire la requête ? Le gros intérêt de ces transformations, c&rsquo;est lorsqu&rsquo;on on fait une requête sur une vue dans laquelle il y a la jointure. La vue va souvent chercher des colonnes dont on a pas besoin. Les transformations permettent alors d&rsquo;éliminer les opérations inutiles.</p>
]]></content:encoded>
			<wfw:commentRss></wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>12c: Index partiel sur tables partitionnées</title>
		<link>https://blog.developpez.com/pachot/12c_partial_index/</link>
		<comments>https://blog.developpez.com/pachot/12c_partial_index/#comments</comments>
		<pubDate>Fri, 27 Sep 2013 06:00:23 +0000</pubDate>
		<dc:creator><![CDATA[pachot]]></dc:creator>
				<category><![CDATA[12c]]></category>
		<category><![CDATA[index]]></category>

		<guid isPermaLink="false">http://blog.developpez.com/pachot/?p=756</guid>
		<description><![CDATA[On n&#8217;a pas toujours besoin d&#8217;indexer toutes les partitions. Dans une table qui garde tout l&#8217;historique, on peut avoir le besoin de n&#8217;indexer que la partition courante, accédées en transactionnel. Les anciennes partitions ont toujours les données, pour des besoins &#8230; <a href="https://blog.developpez.com/pachot/12c_partial_index/">Lire la suite <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<p>On n&rsquo;a pas toujours besoin d&rsquo;indexer toutes les partitions. Dans une table qui garde tout l&rsquo;historique, on peut avoir le besoin de n&rsquo;indexer que la partition courante, accédées en transactionnel. Les anciennes partitions ont toujours les données, pour des besoins de reporting, mais sans devoir stocker tous les index utiles à la partition courante.<br />
Avant la 12c, on peut faire ça avec des index locaux en les rendant unusable (et skip_unusable_indexes=true). Mais pas de solution pour les index globaux &#8211; sinon archiver les anciennes lignes dans une autre table et faire une vue UNION ALL.</p>
<p>En 12c, on peut peut avoir des index partiels:</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">CREATE INDEX ... GLOBAL INDEXING PARTIAL<br />
CREATE INDEX ... LOCAL INDEXING PARTIAL</div></div>
<p>Et on précise au niveau de la tables les partitions qui ne seront pas indexées par les Partial Index:</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">ALTER TABLE ... MODIFY PARTITION ... INDEXING OFF</div></div>
<p>Le résultat:</p>
<ul>
<li>Les Partial Local Index auront leur partitions en &lsquo;indexing off&rsquo; au status UNUSABLE (donc garder la valeur par défaut skip_unusable_indexes=true)</li>
<li>Les Partial Global Index n&rsquo;auront pas d&rsquo;entrée pour les lignes des partitions en &lsquo;indexing off&rsquo;. S&rsquo;il y en avait auparavant, elles deviendront <a href="http://blog.developpez.com/pachot/12c_global_index_maintenance/" target="_blank">orphelines</a></li>
</ul>
<p>L&rsquo;opération inverse elle devra reconstruire les partitions locales, et réindexer les index globaux pour ces partitions.</p>
<p>C&rsquo;est transparent: les SELECT qui utilisent l&rsquo;index feront un UNION ALL pour aller chercher les données indexées via index, et les autres via partition full scan.<br />
Par exemple, lorsque la partition 1 est en &lsquo;indexing off&rsquo;, la partition 2 en &lsquo;indexing on&rsquo;, et l&rsquo;index global en &lsquo;indexing partial':</p>
<pre>------------------------------------------------------------------------------------------------------------------------
| Id  | Operation                                    | Name    | Rows  | Bytes | Cost (%CPU)| Time     | Pstart| Pstop |
------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                             |         |     1 |     8 |    32   (0)| 00:00:01 |       |       |
|   1 |  VIEW                                        | VW_TE_2 |     2 |    52 |    32   (0)| 00:00:01 |       |       |
|   2 |   UNION-ALL                                  |         |       |       |            |          |       |       |
|*  3 |    TABLE ACCESS BY GLOBAL INDEX ROWID BATCHED| TEST    |     1 |     8 |     2   (0)| 00:00:01 |     2 |     2 |
|*  4 |     INDEX RANGE SCAN                         | TEST_N  |     1 |       |     1   (0)| 00:00:01 |       |       |
|   5 |    PARTITION LIST SINGLE                     |         |     1 |     8 |    30   (0)| 00:00:01 |     1 |     1 |
|*  6 |     TABLE ACCESS FULL                        | TEST    |     1 |     8 |    30   (0)| 00:00:01 |     1 |     1 |
------------------------------------------------------------------------------------------------------------------------</pre>
<p><ins datetime="2013-09-24T12:34:23+00:00">L&rsquo;exemple complet dans la <a href="http://ora-demo.pachot.net/12c_partial_index.html" title="demo" target="_blank">demo</a></ins></p>
]]></content:encoded>
			<wfw:commentRss></wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>12c: chaines de caractères &gt;4000 (jusqu&#8217;à 32k)</title>
		<link>https://blog.developpez.com/pachot/12c_extended_data_types/</link>
		<comments>https://blog.developpez.com/pachot/12c_extended_data_types/#comments</comments>
		<pubDate>Mon, 23 Sep 2013 20:20:11 +0000</pubDate>
		<dc:creator><![CDATA[pachot]]></dc:creator>
				<category><![CDATA[12c]]></category>
		<category><![CDATA[4000]]></category>

		<guid isPermaLink="false">http://blog.developpez.com/pachot/?p=744</guid>
		<description><![CDATA[En 12c on peut stocker des chaînes de caractères jusqu&#8217;à 32ko (au lieu de 4000 pour les VARCHAR2 et 2000 pour les CHAR). RAW peut aussi aller jusqu&#8217;à 32ko. Ce n&#8217;est pas activé par défaut. Il faut passer max_string_size=EXTENDED(default STANDARD). &#8230; <a href="https://blog.developpez.com/pachot/12c_extended_data_types/">Lire la suite <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<p>En 12c on peut stocker des chaînes de caractères jusqu&rsquo;à 32ko (au lieu de 4000 pour les VARCHAR2 et 2000 pour les CHAR). RAW peut aussi aller jusqu&rsquo;à 32ko.<br />
Ce n&rsquo;est pas activé par défaut. Il faut passer max_string_size=EXTENDED(default STANDARD).<br />
Et ce n&rsquo;est pas tout. S&rsquo;il y avait des vues (ou colonnes virtuelles) crées avec des expression qui dépassaient les 4000 il faut les recompiler avec un script fourni (utl32k.sql). Pour cette raison la modif se fait en &lsquo;OPEN MIGRATE&rsquo;. Donc la procédure est la suivante:</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">SHUTDOWN IMMEDIATE;<br />
startup upgrade;<br />
ALTER SYSTEM SET max_string_size=extended;<br />
@?/rdbms/admin/utl32k.sql<br />
shutdown immediate;<br />
startup;</div></div>
<p>Si on crée une table avec des extended datatypes, ils seront stockés comme des LOB. Il n&rsquo;y a par contre pas l&rsquo;inconvénient des LOB qui sont sont récupérés un à un (LOBREAD). Ici les 32k peuvent être renvoyés avec la ligne.</p>
<p>Sur une table existante, lorsqu&rsquo;on augmente la taille d&rsquo;une colonne de telle sorte qu&rsquo;elle devienne un extended datatype, les lignes utiliseront le row chaining pour stocker les valeurs plus larges. Il faut donc recréer la table (ce qui peut être fait avec dbms_redefinition) pour avoir un stockage optimal.</p>
<p>On ne peut pas indexer des valeurs plus larges qu&rsquo;un bloc (et même un peu moins &#8211; il faut que tout loge dans une branche d&rsquo;index). Mais la 12c amène une autre amélioration: un index sur SUBSTR(&#8230;,1,&#8230;) peut être utilisé de manière transparente (l&rsquo;optimiseur rajoute un prédicat avec la fonction en question afin de pouvoir utiliser un INDEX RANGE SCAN). On peut aussi utiliser STANDARD_HASH si on interroge uniquement avec une égalité.</p>
<p><ins datetime="2013-09-23T20:02:20+00:00">Tout ça est testé dans la <a href="https://googledrive.com/host/0Bxhrc9V4eGzdd3liTDMwdmFmM2s/12c_extended_data_types.html" title="demo" target="_blank">demo</a></ins></p>
]]></content:encoded>
			<wfw:commentRss></wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>12c: optimisation des appels de fonctions PL/SQL à partir de SQL</title>
		<link>https://blog.developpez.com/pachot/12c_udf_pragma/</link>
		<comments>https://blog.developpez.com/pachot/12c_udf_pragma/#comments</comments>
		<pubDate>Mon, 16 Sep 2013 19:16:11 +0000</pubDate>
		<dc:creator><![CDATA[pachot]]></dc:creator>
				<category><![CDATA[12c]]></category>
		<category><![CDATA[pragma]]></category>
		<category><![CDATA[udf]]></category>
		<category><![CDATA[with_plqsl]]></category>

		<guid isPermaLink="false">http://blog.developpez.com/pachot/?p=703</guid>
		<description><![CDATA[Dans les requêtes SQL, on peut utiliser des fonctions standard (substr, to_date,&#8230; ) et lorsqu&#8217;on a un besoin un peu plus spécifique, on peut créer des des UDF (User Defined Function). Avec Oracle, les fonctions standard sont codées en C, &#8230; <a href="https://blog.developpez.com/pachot/12c_udf_pragma/">Lire la suite <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<p>Dans les requêtes SQL, on peut utiliser des fonctions standard (substr, to_date,&#8230; ) et lorsqu&rsquo;on a un besoin un peu plus spécifique, on peut créer des des UDF (User Defined Function).<br />
Avec Oracle, les fonctions standard sont codées en C, directement exécutable lors de l&rsquo;exécution SQL. Mais les UDF sont simplement des appels à des fonctions PL/SQL stockées. Le problème lorsqu&rsquo;on passe d&rsquo;un language (SQL) à un autre (PL/SQL) en cours d&rsquo;exécution: il y a un switch de contexte à chaque fois. C&rsquo;est très consommateur de CPU. Surtout lorsqu&rsquo;on appelle la fonction pour chaque ligne.</p>
<p>Jusqu&rsquo;en 11g, les solutions sont:</p>
<ul>
<li>Utiliser seulement des fonctions standard</li>
<li>Eviter l&rsquo;appel ligne à ligne avec les pipelined functions</li>
<li>Lorsque le résultat de la fonction est déterministe et qu&rsquo;il est applelé plusieurs fois pour les même valeurs, déclarer la fonction en deterministic</li>
</ul>
<p>En 12c on a des vrais UDF optimisées pour les appels à partir de SQL.</p>
<p>On peut utiliser le pragma UDF pour une fonction stockée, par exemple:</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">create or replace function TEST_SPELL(n number) return varchar2 as pragma UDF; begin return to_char( date'-4712-01-01'+n-1,'Jsp'); end;<br />
/<br />
select max(spelled) from ( select TEST_SPELL(num) spelled from TEST);</div></div>
<p>Et on peut aussi déclarer la fonction comme un bloc PL/SQL anonyme dans la requête SQL:</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">with function TEST_SPELL(n number) return varchar2 as begin return to_char( date'-4712-01-01'+n-1,'Jsp'); end; <br />
select /*+ TEST1 */ max(spelled) from ( select TEST_SPELL(num) spelled from TEST)<br />
&nbsp;/</div></div>
<p><ins datetime="2013-09-16T18:42:15+00:00">Dans la <a href="http://ora-demo.pachot.net/12c_udf_pragma.html" title="demo" target="_blank">demo</a> j&rsquo;appelle une fonction simple pour 1 million de lignes</ins><br />
Dans la version 11g, les temps &lsquo;sql execute elapsed time&rsquo; et &lsquo;PL/SQL execution elapsed time&rsquo; sont considérables, alors que ni la requête ni la fonction ne sont très complexes.<br />
C&rsquo;est parce chacun inclut le switch de contexte vers l&rsquo;autre language.<br />
En utilisant pragma udf ou le with plsql (déjà exposé <a href="http://blog.developpez.com/pachot/12c_with_plsql/">ici</a>) c&rsquo;est une centaine de secondes que l&rsquo;on gagne pour les 1 million d&rsquo;appels.</p>
]]></content:encoded>
			<wfw:commentRss></wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>12c (et 11.2.0.4) : Data Redaction</title>
		<link>https://blog.developpez.com/pachot/12c_data_redaction/</link>
		<comments>https://blog.developpez.com/pachot/12c_data_redaction/#comments</comments>
		<pubDate>Wed, 04 Sep 2013 18:18:43 +0000</pubDate>
		<dc:creator><![CDATA[pachot]]></dc:creator>
				<category><![CDATA[12c]]></category>
		<category><![CDATA[anonymisation]]></category>
		<category><![CDATA[sécurité]]></category>

		<guid isPermaLink="false">http://blog.developpez.com/pachot/?p=663</guid>
		<description><![CDATA[Que faire lorsqu&#8217;on a des données sensitives (numéros de carte de crédit par exemple) qu&#8217;on souhaite masquer sans perturber le fonctionnement de l&#8217;application ? Lorsqu&#8217;on veut changer les données stockées, on peut faire du &#8216;Data Masking': définitivement modifier ces données &#8230; <a href="https://blog.developpez.com/pachot/12c_data_redaction/">Lire la suite <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<p>Que faire lorsqu&rsquo;on a des données sensitives (numéros de carte de crédit par exemple) qu&rsquo;on souhaite masquer sans perturber le fonctionnement de l&rsquo;application ?</p>
<p>Lorsqu&rsquo;on veut changer les données stockées, on peut faire du &lsquo;Data Masking': définitivement modifier ces données sensibles. Par exemple sur une base de test issue de la prod.</p>
<p>Sur une base de prod, on peut utiliser Virtual Private Database pour ne pas afficher certaines colonnes.</p>
<p>Mais la 12c apporte une nouvelle fonctionnalité: Data Redaction</p>
<p>Rien n&rsquo;est modifié dans la base de donnée, mais les valeurs seront masquées lorsqu&rsquo;elles seront envoyées au client. Il y a plusieurs types de masquages différents:</p>
<p>FULL &#8211; le plus simple<br />
Les nombres sont transformés en 0, les dates en 1er janvier de l&rsquo;an 1, les chaines de caractère en un espace. L&rsquo;avantage: on ne change pas le type de données pour que ce soit le plus transparent pour l&rsquo;application.</p>
<p>PARTIAL &#8211; pour personnaliser un peu le masque sur un format spécifique, à la manière des ticket de Carte Bleue (quelques chiffres transformés en &lsquo;*&rsquo;)</p>
<p>REGEXP &#8211; une expression régulière de transformation<br />
On peut par exemple masquer une adresse e-mail tout en gardant un format compatible avec une adresse e-mail. Toujours pour que l&rsquo;application fonctionne lorsqu&rsquo;elle vérifie le format. </p>
<p>RANDOM &#8211; généré par dbms_random pour le type de données. Par exemple pour anonymiser des noms de personnes. Les caractères aléatoires ont l&rsquo;avantage de monter visiblement que ce ne sont pas des vrais noms. Pour des nombres ou des dates, au contraire, c&rsquo;est un moyen de faire passer les valeurs pour des vraies.</p>
<p>NONE &#8211; pour afficher les vraies valeurs par exemple pour une vue sur une table qui a aurait une policy.</p>
<p>Et le Data Redaction peut dépendre d&rsquo;un contexte, par exemple d&rsquo;un role.</p>
<p>Par contre si l&rsquo;application permet de faire des recherches sur la colonne, il sera possible de deviner les valeurs même si elles ne sont pas affichés. Il s&rsquo;agit seulement de modifier les valeurs avant de les retourner dans un résultat. Il faut le coupler avec d&rsquo;autres fonctionnalité pour assurer une véritable sécurité des données.</p>
<p>Tout cela se controle par dbms_redact.<br />
<ins datetime="2013-09-04T13:15:14+00:00">Les différentes syntaxes et exemples dans la <a href="http://ora-demo.pachot.net/12c_data_redaction.html" title="demo" target="_blank">demo</a></ins><br />
Dans la demo, je montre que le role DBA n&rsquo;applique la le Data Redaction. Pour avoir le même comportement en 11.2.0.4 qu&rsquo;en 12c, j&rsquo;ai dû:</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">GRANT EXEMPT REDACTION POLICY &nbsp; &nbsp;TO exp_full_database;</div></div>
<p>car ce n&rsquo;est pas fait par l&rsquo;upgrade 11.2.0.4 (bug probablement puisque contraire à la doc et au fonctionnement de la 12c)</p>
]]></content:encoded>
			<wfw:commentRss></wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>12c: colonnes auto-incrémentées</title>
		<link>https://blog.developpez.com/pachot/12c-identity-columns/</link>
		<comments>https://blog.developpez.com/pachot/12c-identity-columns/#comments</comments>
		<pubDate>Mon, 02 Sep 2013 20:32:54 +0000</pubDate>
		<dc:creator><![CDATA[pachot]]></dc:creator>
				<category><![CDATA[12c]]></category>
		<category><![CDATA[generated]]></category>
		<category><![CDATA[sequence]]></category>

		<guid isPermaLink="false">http://blog.developpez.com/pachot/?p=646</guid>
		<description><![CDATA[En 12c on peut déclarer des colonnes auto-incrémentées en suivant la syntaxe SQL standard: create table TEST ( id number generated always as identity , n number ); Plus besoin de déclarer la sequence, c&#8217;est implicite. Et cette syntaxe empêche &#8230; <a href="https://blog.developpez.com/pachot/12c-identity-columns/">Lire la suite <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<p>En 12c on peut déclarer des colonnes auto-incrémentées en suivant la syntaxe SQL standard:</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">create table TEST ( id number generated always as identity , n number );</div></div>
<p>Plus besoin de déclarer la sequence, c&rsquo;est implicite. Et cette syntaxe empêche de mettre une autre valeur que celle qui sera générée.</p>
<p>En 12c on peut aussi utiliser une sequence comme valeur par défaut (générée seulement lorsqu&rsquo;on ne spécifie pas la valeur). et la syntaxe pour les colonnes quto-incrémentées est la suivante:</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">id number generated by default as identity</div></div>
<p>Enfin, nouvelle feature en 12c, on peut préciser si la valeur par défaut remplace un null:</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">id number generated by default on null as identity</div></div>
<p><ins datetime="2013-09-02T20:12:27+00:00">Les exemples dans la <a href="http://ora-demo.pachot.net/12c-identity-columns.html" title="demo" target="_blank">demo</a></ins></p>
<p>Attention, la colonne est NOT NULL, mais ne déclare pas de primary key (ni d&rsquo;unicité) implicitement.</p>
]]></content:encoded>
			<wfw:commentRss></wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
