Introduction
Qu'est-ce que ER/STUDIO ?
Un outil de conception/modélisation de bases de données relationnelles; cet outil permet le reverse engineering, c'est à dire la rétro-conception.
Ses concurrents les plus connus du marché :
Power AMC
ERWin DataModeler
A quoi cela peut-il servir ?
A la même chose qu'un logiciel Autocad pour un architecte, c'est à dire à séparer la réflexion de conception (le QUOI) de la mise en œuvre (le COMMENT)
Il est d'ailleurs surprenant que, contrairement à ORACLE avec ses outils ORACLE Case, MICROSOFT n'ait pas cherché à pénétrer ce marché avec sa gamme SQLServer ?
Allons-y pour un premier modèle :
Nous parlerons du reverse engineering plus tard, aussi passons à la création de
nouveau modèle.
La barre d'outils est très fournie, mais beaucoup de ces outils ne concernent que des dispositions graphiques.
Pose des entités sur le diagramme :
La pose d'une entité nous demande tout de suite de créer la clé primaire, ce qui est un bon réflexe, connaissant son importance pour les bases de données.
On voit ici les attributs avec le type par défaut de la clé que je viens de créer CHAR(10) (ce type par défaut est modifiable dans une boite de dialogue) pourquoi pas ?
Changeons cela tout de suite par un type plus invariant :
Ceux qui me connaissent savent mon aversion pour les clés significatives, aussi c'est tout naturellement que je choisis un INTEGER ici.
Au niveau conceptuel, ce sont des méta-types dont il s'agit, ils seront traduits en types physiques lors du passage au modèle physique.
Il y a donc un binding entre les méta-types et les types de données cible : cette technique est aussi utilisée par POWER AMC et permet de ne réaliser qu'un modèle conceptuel pour n modèles cibles.
J'ai ainsi procédé dans ma société pendant des années devant produire du SQLServer et de l'Oracle.
Passons aux associations :
Cinq types sont proposés :
. Identifying : ceci permettra d'hériter la clé étrangère dans une partie de clé primaire; en d'autres termes l'identifiant d'un objet inclut celui de son parent
- Non Identifying, Mandatory : c'est du 1 à plusieurs obligatoire
- Non Identifying, Optionnal : c'est du 1 à plusieurs optionnel
- One to one : du 1 à 1 optionnel
- Non specific : c'est du plusieurs à plusieurs
A noter que les propriétés des associations peuvent être affinées ensuite, au delà de ces modèles types.
Voici quelques exemples de ce que l'on peut faire :
. Un client est obligatoirement suivi par un représentant et un seul
. Un représentant peut suivre de 0 à N clients
. Un représentant peut ne pas avoir de compte de prime
. mais un compte de prime doit correspondre à un représentant
. La notion de famille vendue par représentant est en plusieurs-plusieurs , donc une
famille de produit peut être vendue par plusieurs représentants et un représentant peut vendre plusieurs familles de produits
On remarque que les rôles des associations plusieurs-plusieurs (VendeurDe, venduPar) sont mis au départ de l'association.
Cas particuliers
Les associations peuvent être identifiantes comme dans l'exemple ci-dessous :
un compte client sera identifié par le couple (NoEtablissement, NoClient)
En clair : un client a autant de comptes que d'établissements.
On peut avoir des entités qui héritent d'autres entités : on parle de sur-types et de sous-types.
Comme pour les associations identifiantes, le format de l'entité change, passant à un rectangle aux coins arrondis, signalant le caractère particulier de son identifiant.
Par ailleurs, les propriétés de l'héritage sont paramétrables dans cette boite de dialogue :
Nota : sur cet exemple, des intervenants ne sont ni salariés, ni sous-traitants : sans doute des bénévoles....
Cartouche
On peut , pour finaliser le modèle graphique y ajouter un cartouche avec nom de l'auteur, version,...
Passage au modèle physique :
Ce beau (!) modèle étant maintenant complet, il est temps de passer à l'étage inférieur, c'est à dire au modèle physique.
Le modèle a été généré :
Notre fusée a maintenant deux étages :
. un étage conceptuel (Logical)
. un étage physique (Base commerciale)
On voit sur ce modèle graphique que les clés étrangères ont été générées lorsque c'était nécessaire; en particulier pour la table CompteClient.
Par ailleurs, l'association plusieurs-plusieurs Representant-FamilleProduit a bien
généré une table d'association avec la PK constituée de la concaténation des deux FK associées.
Génération du script SQL de création de la base :
Nous pouvons voir/lancer le script généré :
Cela me rappelle étrangement quelque chose, mais quoi ??(!)
Voici, in extenso, le contenu de ce script :
/*
* ER/Studio 7.1 SQL Code Generation
* Company : Dialogue Formation Informatique
* Project : ClientRepresentant.DM1
* Author : Dominique
*
* Date created : Friday, April 20, 2007 11:56:04
* Target DBMS : Microsoft SQL Server 2005
*/
/*
* table: Client
*/
create table Client(
NoClient int NOT null,
Nom varchar(80) null,
AdresseRue varchar(80) null,
CodePostal varchar(10) null,
Ville varchar(80) null,
NoRepresentant int NOT null,
CONSTRAINT PK1 primary key clustered (NoClient)
)
go
if OBJECT_ID('Client') IS NOT null
print '<<< createD table Client >>>'
else
print '<<< FAILED CREATING table Client >>>'
go
/*
* table: CompteClient
*/
create table CompteClient(
NoEtablissement int NOT null,
NoClient int NOT null,
CONSTRAINT PK7 primary key clustered (NoEtablissement, NoClient)
)
go
if OBJECT_ID('CompteClient') IS NOT null
print '<<< createD table CompteClient >>>'
else
print '<<< FAILED CREATING table CompteClient >>>'
go
/*
* table: CompteDePrime
*/
create table CompteDePrime(
NoCompte int NOT null,
NoRepresentant int NOT null,
CONSTRAINT PK3 primary key clustered (NoCompte)
)
go
if OBJECT_ID('CompteDePrime') IS NOT null
print '<<< createD table CompteDePrime >>>'
else
print '<<< FAILED CREATING table CompteDePrime >>>'
go
/*
* table: Etablissement
*/
create table Etablissement(
NoEtablissement int NOT null,
NomEtablissement varchar(80) null,
CONSTRAINT PK6 primary key clustered (NoEtablissement)
)
go
if OBJECT_ID('Etablissement') IS NOT null
print '<<< createD table Etablissement >>>'
else
print '<<< FAILED CREATING table Etablissement >>>'
go
/*
* table: FamilleProduit
*/
create table FamilleProduit(
NoFamilleProduit int NOT null,
Libelle varchar(80) null,
CONSTRAINT PK4 primary key clustered (NoFamilleProduit)
)
go
if OBJECT_ID('FamilleProduit') IS NOT null
print '<<< createD table FamilleProduit >>>'
else
print '<<< FAILED CREATING table FamilleProduit >>>'
go
/*
* table: [FamilleProduit Representant]
*/
create table [FamilleProduit Representant](
NoFamilleProduit int NOT null,
NoRepresentant int NOT null,
CONSTRAINT PK5 primary key clustered (NoFamilleProduit, NoRepresentant)
)
go
if OBJECT_ID('FamilleProduit Representant') IS NOT null
print '<<< createD table FamilleProduit Representant >>>'
else
print '<<< FAILED CREATING table FamilleProduit Representant >>>'
go
/*
* table: intervenant
*/
create table intervenant(
Nointervenant int NOT null,
Typeintervenant char(1) null,
CONSTRAINT PK8 primary key clustered (Nointervenant)
)
go
if OBJECT_ID('intervenant') IS NOT null
print '<<< createD table intervenant >>>'
else
print '<<< FAILED CREATING table intervenant >>>'
go
/*
* table: Representant
*/
create table Representant(
NoRepresentant int NOT null,
Nom varchar(80) null,
Prenom varchar(80) null,
CONSTRAINT PK2 primary key clustered (NoRepresentant)
)
go
if OBJECT_ID('Representant') IS NOT null
print '<<< createD table Representant >>>'
else
print '<<< FAILED CREATING table Representant >>>'
go
/*
* table: Salarie
*/
create table Salarie(
Nointervenant int NOT null,
NoSalarie int NOT null,
CONSTRAINT PK9 primary key clustered (Nointervenant)
)
go
if OBJECT_ID('Salarie') IS NOT null
print '<<< createD table Salarie >>>'
else
print '<<< FAILED CREATING table Salarie >>>'
go
/*
* table: SousTraitant
*/
create table SousTraitant(
Nointervenant int NOT null,
NoSousTraitant int NOT null,
CONSTRAINT PK10 primary key clustered (Nointervenant)
)
go
if OBJECT_ID('SousTraitant') IS NOT null
print '<<< createD table SousTraitant >>>'
else
print '<<< FAILED CREATING table SousTraitant >>>'
go
/*
* INDEX: Ref21
*/
create INDEX Ref21 ON Client(NoRepresentant)
go
if EXISTS (select * from sysindexes where id=OBJECT_ID('Client') AND name='Ref21')
print '<<< createD INDEX Client.Ref21 >>>'
else
print '<<< FAILED CREATING INDEX Client.Ref21 >>>'
go
/*
* INDEX: Ref66
*/
create INDEX Ref66 ON CompteClient(NoEtablissement)
go
if EXISTS (select * from sysindexes where id=OBJECT_ID('CompteClient') AND name='Ref66')
print '<<< createD INDEX CompteClient.Ref66 >>>'
else
print '<<< FAILED CREATING INDEX CompteClient.Ref66 >>>'
go
/*
* INDEX: Ref17
*/
create INDEX Ref17 ON CompteClient(NoClient)
go
if EXISTS (select * from sysindexes where id=OBJECT_ID('CompteClient') AND name='Ref17')
print '<<< createD INDEX CompteClient.Ref17 >>>'
else
print '<<< FAILED CREATING INDEX CompteClient.Ref17 >>>'
go
/*
* INDEX: Ref22
*/
create INDEX Ref22 ON CompteDePrime(NoRepresentant)
go
if EXISTS (select * from sysindexes where id=OBJECT_ID('CompteDePrime') AND name='Ref22')
print '<<< createD INDEX CompteDePrime.Ref22 >>>'
else
print '<<< FAILED CREATING INDEX CompteDePrime.Ref22 >>>'
go
/*
* INDEX: Ref44
*/
create INDEX Ref44 ON [FamilleProduit Representant](NoFamilleProduit)
go
if EXISTS (select * from sysindexes where id=OBJECT_ID('FamilleProduit Representant') AND name='Ref44')
print '<<< createD INDEX FamilleProduit Representant.Ref44 >>>'
else
print '<<< FAILED CREATING INDEX FamilleProduit Representant.Ref44 >>>'
go
/*
* INDEX: Ref25
*/
create INDEX Ref25 ON [FamilleProduit Representant](NoRepresentant)
go
if EXISTS (select * from sysindexes where id=OBJECT_ID('FamilleProduit Representant') AND name='Ref25')
print '<<< createD INDEX FamilleProduit Representant.Ref25 >>>'
else
print '<<< FAILED CREATING INDEX FamilleProduit Representant.Ref25 >>>'
go
/*
* INDEX: Ref88
*/
create INDEX Ref88 ON Salarie(Nointervenant)
go
if EXISTS (select * from sysindexes where id=OBJECT_ID('Salarie') AND name='Ref88')
print '<<< createD INDEX Salarie.Ref88 >>>'
else
print '<<< FAILED CREATING INDEX Salarie.Ref88 >>>'
go
/*
* INDEX: Ref89
*/
create INDEX Ref89 ON SousTraitant(Nointervenant)
go
if EXISTS (select * from sysindexes where id=OBJECT_ID('SousTraitant') AND name='Ref89')
print '<<< createD INDEX SousTraitant.Ref89 >>>'
else
print '<<< FAILED CREATING INDEX SousTraitant.Ref89 >>>'
go
/*
* table: Client
*/
alter table Client ADD CONSTRAINT RefRepresentant1
FOREIGN key (NoRepresentant)
REFERENCES Representant(NoRepresentant)
go
/*
* table: CompteClient
*/
alter table CompteClient ADD CONSTRAINT RefEtablissement6
FOREIGN key (NoEtablissement)
REFERENCES Etablissement(NoEtablissement)
go
alter table CompteClient ADD CONSTRAINT RefClient7
FOREIGN key (NoClient)
REFERENCES Client(NoClient)
go
/*
* table: CompteDePrime
*/
alter table CompteDePrime ADD CONSTRAINT RefRepresentant2
FOREIGN key (NoRepresentant)
REFERENCES Representant(NoRepresentant)
go
/*
* table: [FamilleProduit Representant]
*/
alter table [FamilleProduit Representant] ADD CONSTRAINT RefFamilleProduit4
FOREIGN key (NoFamilleProduit)
REFERENCES FamilleProduit(NoFamilleProduit)
go
alter table [FamilleProduit Representant] ADD CONSTRAINT RefRepresentant5
FOREIGN key (NoRepresentant)
REFERENCES Representant(NoRepresentant)
go
/*
* table: Salarie
*/
alter table Salarie ADD CONSTRAINT Refintervenant8
FOREIGN key (Nointervenant)
REFERENCES intervenant(Nointervenant)
go
/*
* table: SousTraitant
*/
alter table SousTraitant ADD CONSTRAINT Refintervenant9
FOREIGN key (Nointervenant)
REFERENCES intervenant(Nointervenant)
go
Altération de schéma :
Oups ! Cela se corse...
Apparemment, il n'y a pas de moyen facile de générer des altérations de schémas SQL; c'est à dire que, en rajoutant deux colonnes à une table par exemple, ER/Studio devrait me générer les ordres SQL ALTER TABLE nécessaires...
Là, on peut dire que POWER AMC est loin devant, il fait cela depuis des années, avec en plus des fonctionnalités sympathiques du style passage par des tables temporaires lorsque l'instruction DDL nécessaire n'est pas automatisable.
Ce produit (en tout cas la version d'évaluation dont je dispose à ce jour) ne sait donc gérer que la naissance de la base de données, ce que je considère comme grandement insuffisant : les évolutions ultérieures peuvent durer des années.
A suivre : si quelqu'un a plus d'infos, qu'il le fasse savoir !
Reverse Engineering
Il s'agit cette fois d'analyser une base EXISTANTE et de remonter les modèles à partir du schéma relationnel en place.
Connectons-nous en natif à une base SQLServer 2005 existante :
Vision partielle du modèle rétro-analysé :
Pas si mal !
Les liens identifiants ont bien été retrouvés, les types personnalisés natif SQLServer récupérés, par contre les types personnalisés .Net (il faut que j'écrive un article sur cette possibilité) n'ont pas été remontés : logique, vu le caractère hyper spécifique de ces types.
Les règles de mise à jour des intégrités référentielles sont aussi reprises :
On voit ici une suppression en cascade :
Conclusion :
Ce produit est facile à prendre en main, la plupart des menus et actions étant intuitifs.
Maintenant, comme déjà signalé je regrette l'absence d'un vrai versioning qui permettrait de générer les altérations de schéma : cela est peut être du à la version que j'utilise.
Si je devais comparer ce produit à Power AMC je lui mettrais un satisfecit pour l'ergonomie (il est vrai que certaines choses sont un peu 'space' dans Power AMC), mais un bémol au niveau fonctionnel : par exemple, dans les choix de présentation graphique, la liste des attributs à afficher.
Maintenant, il faut comparer des choses comparables et il est clair que Power AMC reste un produit très coûteux...