Prouvez-le !

J’ai lu un blog aujourd’hui qui parle des lignes chainées avec Oracle et qui dit que cela arrive lorsqu’on utilise des LOB. Je ne référencerai pas cet article parce que son auteur ne le mérite pas mais le fait est que l’utilisation d’un LOB, même d’une taille supérieure à la taille d’un bloc, ne suffit pas à générer des lignes chainées avec Oracle.

Bien sur, vous pouvez lire la documentation à propos des listes chainées et vous verrez qu’il n’y est pas fait mention des LOB mais seulement des LONG et LONG RAW. Cela dit, ce n’est pas parce que la documentation ne dit pas quelque chose que c’est faux ! Pas vrai ?

Il y a plusieurs façons de comprendre quelque chose et Oracle étant, malgré tout, loin de l’astrophysique des approches simples fonctionnent très bien. Vous pouvez utiliser une approche inductive (strace, 10046, ou dans ce cas, faire un dump des blocs de données avec la commande alter system dump datafile ...). Vous pouvez aussi suivre un raisonnement hypothético-déductif comme ci-dessous. Enfin, vous pouvez, comme le MI-6, évaluer chaque information selon sa probabilité (likelyhood) et la fiabilité de la source (documentation 98%, J. Lewis 99.9%). Vous pourriez donc juste me croire. Cela suppose toutefois que vous soyez capable d’appréhender la probabilité de cette information ou que je sois une source fiable…

Au final, une approche déductive – vive les mathématiques ! – est une bonne façon d’aborder de nombreux aspects d’Oracle. Vous n’aurez pour cela besoin de rien d’autre que vous-même et de l’envie de prouver vos hypothèses. Alors pouvons que si vous stockez une données dans un LOB, la ligne correspondante n’est pas chainée au sens Oracle du terme. Nous allons commencer, pour fixer les idées, avec une table qui contient un LONG:

connect system/manager

create table T
(id number,
text long);
/

J’ai écrit un programme Perl qui utilise DBD::Oracle pour insérer une données de 8000 caractères dans la colonne LONG ou CLOB:

#!/bin/perl
use strict;
use DBI;

my $dbh = DBI->connect('DBI:Oracle:ORCL',
'system', 'manager',
{ AutoCommit => 1})
or die "Cannot connect : " . DBI->errstr;

$dbh->{LongReadLen} = 10000;
my $mylong = 'K' x ( 8000 );

my $sth = $dbh->prepare(
'insert into t values (1,?)')
or die "Cannot create statement: "
. $dbh->errstr;

$sth->execute( $mylong )
or die "Cannot execute statement: "
. $sth->errstr;

$sth = $dbh->prepare(
'select id, text from t')
or die "Cannot create statement: "
. $dbh->errstr;

$sth->execute()
or die "Cannot execute statement: "
. $sth->errstr;

my @data;
while (@data = $sth->fetchrow_array()) {
my $id = $data[0];
my $text = $data[1];
print "t Id: $id n";
print "tText: $text n";
}

$sth->finish;
$dbh->disconnect;

Je lance le programme et regarde le nombre de lignes chainées pour ma table T:

$ perl test.pl

sqlplus system/manager

@?/rdbms/admin/utlchain.sql
truncate table chained_rows;

analyze table t list chained rows;

col table_name format a5
select table_name, head_rowid
from chained_rows;

TABLE HEAD_ROWID
----- ------------------
T AAASDDAABAAAWz6AAA

Maintenant, refaisons le test avec un CLOB:

sqlplus system/manager

drop table T purge;

create table T
(id number,
text clob);

exit;

perl test.pl

sqlplus system/manager

truncate table chained_rows;

analyze table t list chained rows;

col table_name format a5
select table_name, head_rowid
from chained_rows;

no rows selected

Et voilà, malgré l’insertion d’un champs CLOB de 8000 caractères, la ligne n’apparait pas dans la liste des lignes chainées et migrées de la table. La raison en est que le champ est stocké au moins en partie dans un autre segment de la base de données, ce qui n’est pas le cas pour un LONG:

select segment_name
from user_lobs
where table_name='T';

SEGMENT_NAME
-------------------------
SYS_LOB0000073926C00002$$