Préambule▲
Les bases de données relationnelles supposent l'existence de relations entre les données contenues dans des tables, par exemple pour y effectuer des jointures ou des filtres. La performance d'une base de données est alors sans commune mesure avec ce qu'un développeur pourrait faire en n'utilisant que les ressources d'un langage de programmation et de simples fichiers de données. À l'inverse, le stockage de données dont le filtrage ou la jointure ne peut s'opérer directement en SQL diminue très lourdement les performances des SGBDR et constitue un non-sens… Compte tenu du volume important des images, la base se trouve alors polluée avec une quantité d'informations non négligeable dont seule une petite partie est réellement utilisée par le moteur SQL.
Allez-vous un jour poser au moteur SQL de votre SGBDR la requête :
SELECT
Image
FROM
TableImage
WHERE
Image
contient 'un chien jaune sur une balançoire'
???
Non bien sûr ! Donc il est inutile de s'amuser à stocker des images dans une base de données, d'autant que la manipulation des BLOBS (les colonnes spécialisées pour ce genre de données) ne peut pas la plupart du temps faire l'objet de requêtes SQL basiques…
Il ne faut pas oublier non plus que ce que fait le mieux un ordinateur, c'est justement la manipulation des fichiers… Il y a donc tout à gagner à utiliser un stockage sous forme de fichiers dans l'OS plutôt que de manipuler des flux binaires dans des colonnes de type BLOB.
1. Étude des caractéristiques des images▲
En revanche, les images possèdent des caractéristiques (que l'on nomme attributs lorsque l'on modélise) qu'il est souvent utile de collecter dans une base de données.
Voici quelques-unes des principales caractéristiques des images :
FORMAT (CHAR 4) |
BMP, JPEG, TIF, GIF… |
COULEURS (INTEGER) |
2 (noir & blanc), 256 (niveaux de gris), 1024 (couleurs) … |
LARGEUR (INTEGER) |
la largeur de l'image |
HAUTEUR (INTEGER) |
la hauteur de l'image |
NOM (VARCHAR 256) |
le nom du fichier |
TAILLE (INTEGER) |
le volume des données |
DESCRIPTION (VARCHAR 1024) |
une description de l'image pouvant faire l'objet d'une requête LIKE |
LEGENDE (VARCHAR 256) |
légende s'inscrivant sous l'image |
HINT (VARCHAR 256) |
information apparaissant au survol de la souris |
COPYRIGHT (VARCHAR 256) |
auteur / propriétaire de l'image |
DATE CREATION |
date de création |
Bien entendu on peut rajouter différentes caractéristiques répondant à ses propres besoins…
Cette approche est souvent appelée technique des métadonnées, parce que l'on ne travaille pas directement sur la donnée, mais sur les caractéristiques, les paramètres de la donnée.
2. Stockage des images▲
On peut stocker toutes ses images dans un seul et même répertoire d'un serveur de fichiers. Il suffit de connaître le chemin de ce répertoire qui peut être fixe. Appelons-le « PATH_IMAGE ». Dès lors, pour retrouver l'emplacement d'une image précise, il suffit de concaténer la constante PATH_IMAGE au nom de l'image.
Exemple :
PATH_IMAGE := '\\SRV_files\images\'
BITMAP_FILE := '\\SRV_files\images\' + NOM
et le tour est joué !
Mais cette technique simpliste possède un inconvénient majeur : il peut y avoir trop de fichiers dans le répertoire… N'oublions pas que les systèmes d'exploitation possèdent des limites et même si elles sont très larges, il n'est jamais impossible de les atteindre.
Une autre solution consiste à multiplier le nombre de répertoires et d'y stocker les images, soient par leurs dates d'insertion, soit par un volume ou un nombre prédéfini.
Par date |
par exemple par mois + année. Exemple : |
Par nombre |
par exemple en limitant à 256 images |
Par volume |
par exemple en limitant le volume des images à 600 Mo |
Pour ma part je choisis en général la technique du volume, en la limitant à 600 Mo. Pourquoi ? Parce que 600 Mo c'est un CD Rom et qu'il m'est donc très facile pour l'archivage de graver un CD d'images par répertoire !
Dès lors se pose le problème de savoir où chaque image se situe. Il suffit de rajouter aux caractéristiques déjà spécifiées, le chemin de stockage de chaque image, chemin dont la référence en clair peut être stockée dans une autre table.
3. Un premier modèle de données▲
Voici un premier modèle conceptuel des données :
Vous noterez que j'ai rajouté une table de référence pour le format des images (T_TYPE_IMAGE_TIM), cela me permet de créer préventivement tous les formats avec lesquels je désire travailler, ne serait-ce qu'à des fins de contrôles et de validation des données.
Il me faut cependant une variable globale de départ, celle du point d'entrée dans le système de fichier (le « path ») à partir duquel je vais pouvoir créer mes répertoires de stockage. Cela peut être fait dans un fichier INI, dans un outil spécifique au système (la registry Windows par exemple, mais alors plus de portabilité vers un autre OS !) ou bien encore (et c'est ce que je préfère) dans une table de paramétrage de ma base de données…
4. La gestion de l'obsolescence▲
Une chose intéressante est de pouvoir gérer l'obsolescence des images. Autrement dit, gérer une image qui n'est plus utilisée, ou bien encore remplacée par une autre.
Pour la suppression, rien de plus simple il suffit de créer une colonne supplémentaire dans la table T_IMAGE_IMG, afin d'y mettre la date de suppression. Ainsi toute requête pour retrouver une image filtrera sur cette date et celle du système. Cela présente l'avantage indéniable de permettre de restituer des pages web obsolètes (archives par exemple) avec les images d'origine plutôt qu'avec des « trous ». Sachez aussi, que de manière générale on évite en matière de bases de données de supprimer les informations.
Pour le remplacement, il suffit de relier la table T_IMAGE à elle-même afin d'informer que l'image visée a été remplacée par une nouvelle. Une simple autoréférence suffit en général. Il faudra donc veiller à ce que cette colonne soit NULL dans les requêtes de recherche, et à défaut, rechercher l'image de remplacement.
Voici donc un modèle plus complet de l'organisation de notre système de gestion des métadonnées des images :
Et un modèle physique équivalent en SQL 2 :
À noter que l'autoréférence sur la table des images pour gérer l'obsolescence a été définie par une colonne de nom IMG_ID_REMPLACEMENT.
5. Script SQL complet▲
Voici un script SQL complet pour la création d'une telle base et de son référentiel de données :
-- CRÉATION DES TABLES
-- ============================================================
-- Table : T_PARAM_BASE_PRB
-- ============================================================
create
table
T_PARAM_BASE_PRB
(
PRB_ID INTEGER
not
null
,
PRB_NOM CHAR
(
256
)
not
null
,
PRB VARCHAR
(
256
)
not
null
,
primary
key
(
PRB_ID)
)
;
-- ============================================================
-- Index : T_PARAM_BASE_PRB_PK
-- ============================================================
create
unique
index
T_PARAM_BASE_PRB_PK on
T_PARAM_BASE_PRB (
PRB_ID asc
)
;
-- ============================================================
-- Table : T_TYPE_IMAGE_TIM
-- ============================================================
create
table
T_TYPE_IMAGE_TIM
(
TIM_ID INTEGER
not
null
,
TIM_FORMAT CHAR
(
4
)
not
null
,
TIM_LIBELLE VARCHAR
(
32
)
not
null
,
primary
key
(
TIM_ID)
)
;
-- ============================================================
-- Index : T_TYPE_IMAGE_TIM_PK
-- ============================================================
create
unique
index
T_TYPE_IMAGE_TIM_PK on
T_TYPE_IMAGE_TIM (
TIM_ID asc
)
;
-- ============================================================
-- Table : T_STOCKAGE_IMAGE_SIM
-- ============================================================
create
table
T_STOCKAGE_IMAGE_SIM
(
SIM_ID INTEGER
not
null
,
SIM_DATE_CREATION DATE
not
null
,
SIM_PATH VARCHAR
(
1024
)
not
null
,
primary
key
(
SIM_ID)
)
;
-- ============================================================
-- Index : T_STOCKAGE_IMAGE_SIM_PK
-- ============================================================
create
unique
index
T_STOCKAGE_IMAGE_SIM_PK on
T_STOCKAGE_IMAGE_SIM (
SIM_ID asc
)
;
-- ============================================================
-- Table : T_IMAGE_IMG
-- ============================================================
create
table
T_IMAGE_IMG
(
IMG_ID INTEGER
not
null
,
TIM_ID INTEGER
not
null
,
IMG_ID_REMPLACEMENT INTEGER
,
IMG_NOM_FICHIER VARCHAR
(
256
)
not
null
,
IMG_DATE_CREATION DATE
,
IMG_COULEURS INTEGER
,
IMG_LARGEUR INTEGER
,
IMG_HAUTEUR INTEGER
,
IMG_TAILLE INTEGER
,
IMG_DESCRIPTION VARCHAR
(
1024
)
,
IMG_LEGENDE VARCHAR
(
256
)
,
IMG_HINT VARCHAR
(
256
)
,
IMG_COPYRIGHT VARCHAR
(
256
)
,
IMG_DATE_SUPPRESSION DATE
,
SIM_ID INTEGER
not
null
,
primary
key
(
IMG_ID)
)
;
-- ============================================================
-- Index : T_IMAGE_IMG_PK
-- ============================================================
create
unique
index
T_IMAGE_IMG_PK on
T_IMAGE_IMG (
IMG_ID asc
)
;
-- ============================================================
-- Index : T_IMAGE_NOM_UNI
-- ============================================================
create
unique
index
T_IMAGE_NOM_UNI on
T_IMAGE_IMG (
IMG_NOM_FICHIER asc
)
;
-- ============================================================
-- Index : TJ_IMGTIM_FK
-- ============================================================
create
index
TJ_IMGTIM_FK on
T_IMAGE_IMG (
TIM_ID asc
)
;
-- ============================================================
-- Index : TJ_IMGIMG_FK
-- ============================================================
create
index
TJ_IMGIMG_FK on
T_IMAGE_IMG (
IMG_ID_REMPLACEMENT asc
)
;
-- ============================================================
-- Index : TJ_IMGSIM_FK
-- ============================================================
create
index
TJ_IMGSIM_FK on
T_IMAGE_IMG (
SIM_ID asc
)
;
alter
table
T_IMAGE_IMG
add
foreign
key
(
TIM_ID)
references
T_TYPE_IMAGE_TIM (
TIM_ID)
;
alter
table
T_IMAGE_IMG
add
foreign
key
(
IMG_ID_REMPLACEMENT)
references
T_IMAGE_IMG (
IMG_ID)
;
alter
table
T_IMAGE_IMG
add
foreign
key
(
SIM_ID)
references
T_STOCKAGE_IMAGE_SIM (
SIM_ID)
;
-- insertion des formats de fichiers d'image prédéfinis
INSERT
INTO
T_TYPE_IMAGE_TIM (
TIM_ID, TIM_FORMAT, TIM_LIBELLE)
VALUES
(
1
, 'BMP '
, 'Microsoft bitmap'
)
INSERT
INTO
T_TYPE_IMAGE_TIM (
TIM_ID, TIM_FORMAT, TIM_LIBELLE)
VALUES
(
2
, 'GIF '
, 'Compuserve GIF'
)
INSERT
INTO
T_TYPE_IMAGE_TIM (
TIM_ID, TIM_FORMAT, TIM_LIBELLE)
VALUES
(
3
, 'JPG '
, 'Internet compressé'
)
INSERT
INTO
T_TYPE_IMAGE_TIM (
TIM_ID, TIM_FORMAT, TIM_LIBELLE)
VALUES
(
4
, 'JPEG '
, 'Internet compressé'
)
Vous aurez peut-être remarqué que j'ai ajouté subrepticement un index unique sur la colonne IMG_NOM_FICHIER afin d'éviter de tenter d'insérer deux fichiers d'images portant le même nom !
6. Aller plus loin▲
Pourquoi ne pas gérer des droits d'accès aux images en utilisant cette technique de métadonnées ?
Simple, il suffit d'utiliser une table des utilisateurs (ou en créer une) avec des droits d'accès (lecture par exemple) et de créer une table de jointure entre la table des images et celle des utilisateurs.
Il existe encore de nombreuses autres possibilités d'exploitation d'un tel modèle et de la technique des données. Je laisse à votre sagacité le soin de le compléter en fonction de vos besoins.
Bien entendu je serais intéressé que vous me fassiez part de vos expériences en la matière…
Au fait, un tel modèle peut aussi être utilisé pour gérer des fichiers de son ou de vidéo… Et pourquoi pas des images enrichies, comme des bitmap vectoriels pour de la cartographie interactive par exemple ?
7. Discussion sur la solution…▲
À propos de : « Faut-il insérer des images directement dans une colonne BLOB d'une table ? »
Une discussion a eu lieu sur Internet dans le forum consacré à SQL Server… En voici l'essentiel
David : |
Sélectionnez
|
Med : |
Sélectionnez
|
David : |
Sélectionnez
|
Med : |
Sélectionnez
|
David : |
Sélectionnez
|
Med : |
Sélectionnez
|
Fred (SQLpro) : |
Sélectionnez
|
Denis |
Sélectionnez
|