Tutorial Interbase - John COLIBRI. | - mots clé:tutorial - Interbase - Client Serveur
- logiciel utilisé: Windows XP, Delphi 6, Interbase 6
- matériel utilisé: Pentium 1.400Mhz, 256 M de mémoire
- champ d'application: Delphi 1 à 7 sur Windows, Kylix
- niveau: débutant Delphi et Base de Données
- plan:
1 - Introduction Ce tutorial va vous indiquer comment utiliser le Serveur Interbase fourni avec Delphi. Ce tutorial a plusieurs objectifs:
- vous présenter intégralement comment réaliser des applications utilisant des bases de données Sql en Delphi. Tous les exemples ont été codés et les sources sont téléchargeables.
- effectuer toutes les opérations en Delphi, plutôt que de présenter une série d'outils annexes dont la présentation alongerait l'exposé
- insister sur les points qui posent problème (création d'une base, chaîne de
connection, modification de dbGrid) plutôt que sur les composants visuels assez intuitifs au demeurant
- présenter les composants réellement utilisés pour gérer des tables Sql (des
tIbQuery) plutôt que des composants génériques (tIbTable) dont les traitements automatiques masquent le fonctionnement réel d'un Serveur Sql
- limiter au maximum le nombre de composants utilisés pour manipuler les
données du Serveur sans perdre en fonctionnalité
Si après ce premier contact vous souhaitez approfondir vos connaissances, nous vous proposons soit des formations ou des prestations de
programmation et d'assistance. Nous nous adressons pour ce tutorial à un programmeur Delphi: - ayant une idée élémentaire de Delphi: Palette, Inspecteur, OnClick. Tout le reste sera expliqué
- n'ayant pas nécessairement de connaissance SQL. Le fonctionnement des requêtes et leur syntaxe sera présenté
2 - Installation 2.1 - Principe
Le logiciel Interbase comporte essentiellement deux parties: - le programme Serveur (le moteur), qui en général est placé sur un PC distant
- le programme Client, qui dialogue avec les programme applicatifs (Delphi ou autre).
Il faut donc installer les deux parties (sur la même machine ou sur des PC séparés). Interbase peut fonctionner selon deux modes:
- le mode dit "local", pour lequel le moteur et le programme client sont sur le même PC. La machine n'a pas besoin d'avoir les couches réseau (TCP\IP, Novell ou autre), ni de carte réseau (carte Ethernet ou liaison série). Le
programme applicatif communique avec le Client, qui appelle directement les routines du serveur (sans passer par des appels TCP\IP)
- le mode Client Serveur
- les communications entre la DLL client et le Serveur passent par les couches réseau de Windows: il faut que ces couches réseau soient installées (même si le Serveur et le Client sont sur le même PC)
- le programme applicatif devra désigner le serveur en utilisant l'adresse IP du serveur.
Dans les deux cas, l'installation se fait en utilisant l'installateur du CD
Delphi (ou du CD Interbase). Nous présenterons l'instalation à partir du CD Delphi.
2.2 - Installation avec Delphi Lors de l'installation de Delphi, Installshield propose d'installer aussi
Interbase. Vous pouvez répondre oui et Interbase sera installé Si vous avez répondu "Non", voici comment procéder:
2.3 - Installation du Mode Local Pour installer Interbase en mode local:
2.4 - Installation du Mode Distant
Installez le Serveur sur le PC serveur, en procédant comme ci-dessus, mais sélectionnez "Interbase 6 Server". La partie Client installée sur le PC du Serveur sera utilisée pour les logiciels de gestion du Serveur
Installez le Client sur le PC client:
2.5 - Suppression d'Interbase Pour supprimer Interbase: |
ouvrez le Serveur Manager et cliquez "Stop" (pour arrêter le moteur) | | ouvrez le panneau de configuration, sélectionnez "Ajout / Suppression de
logiciel", sélectionnez "Interbase" et cliquez Oui partout |
3 - Créer la Base 3.1 - SQL et la création de bases
Pour créer une base, il faut, pour tous les moteurs, utiliser des outils spéciaux, le langage SQL ne contenant pas de requête spécifique pour cette opération Pour Interbase, ce sont des primitives de l'API native du moteur qui permettent
cette création. Elle n'est pas possible depuis les composants générique d'accès aux données (tTable, tQuery). En revanche, Jeff Overcash a ajouté à tIbDatabase la méthode qui utilise les API Interbase natifs et permet la
création de la base. Les éléments à fournir sont les suivants; - le chemin DOS où sera placé le fichier .GDB
- le dialecte Interbase à utiliser. En gros, le dialecte détermine le
traitement de certains types de données Interbase (en dialecte 3 nous pouvons utiliser des Integer 64 bits et des tTimeStamps, minuscule ou majuscule sont reconnus etc). Nous choisirons le dialecte 3 (le plus récent).
- le nom d'utilisateur et le mot de passe du Serveur. Ce sont les noms du serveur par défaut (la base n'étant pas encore créée, elle)
- des paramètres d'optimisation, tels que la taille des pages du cache
Plus précisément:
- le chemin est placé dans tIbDatabase.DataBaseName
- le dialecte est placé dans tIbDatabase.SqlDialect
- le nom d'utilisateur et le mot de passe, ainsi que les autres paramètres
sont placés dans la propriété fourre-tout Params
- la méthode de création est simplement:
tIbDatabase.CreateDataBase |
3.2 - Création de la base
Nous allons créer une base contenant les stages offerts par l'Institut Pascal. Nous placerons cette base dans le répertoire "..\data\" (situé au même niveau que le .EXE. Le .ZIP téléchargeable créera ce répertoire automatiquement). Le
nom du fichier sera "Institut_Pascal.GDB". Pour créer la base: | créez une nouvelle application et appelez-la "p_ib_create_base" |
| sélectionnez dans la page "Interbase" de la Palette le composant tIbTransaction: et posez ce composant sur la tForm | |
sélectionnez de même une tIbDatabase et placez-la sur la tForm
| | initialisez IbDatabase.DefaultTransaction vers IbTransaction1 |
| utilisez un tButton pour créer la base: - placez un tButton, nommez-le create_base_, et créez sa méthode OnClick
- voici le code de cette méthode:
procedure TForm1.create_database_Click(Sender: TObject);
begin
with IbDatabase1 do
begin
DatabaseName:= '..\data\Institut_Pascal.gdb';
SqlDialect:= 3;
Params.Add('USER "SYSDBA"');
Params.Add('PASSWORD "masterkey"');
Params.Add('PAGE_SIZE 4096');
CreateDatabase;
end; // with IbDataBase1
end; // create_database_Click | |
| compilez et exécutez | | cliquez Button1 |
| le répertoire ..\data\ contient bien le fichier "Institut_Pascal.GDB" dont la taille est d'environ 580 K | Notez que:
- si le fichier formation.gdb existait déjà il faut tester sa présence (FileExists) et l'effacer (Erase)
- si nous souhaitons répéter la création, il faudrait purger les Params avant
tout ajout par Add (Params.Clear)
Vous pouvez télécharger ce projet "ib_create_base.zip".
3.3 - Connection à une base
tIbDatabase est utilisé par la suite pour assurer les échanges entre notre application et la base que nous venons de créer. Ce composant doit être initialisé avec: - dans DataBaseName, la chaîne de connection qui est
- si nous travaillons en local, le chemin:
c:\programs\interbase\data\Institut_Pascal.gdb ou ..\data\Institut_Pascal.gdb
- l'URL du PC qui héberge le Serveur:
127.0.0.1\ c:\programs\interbase\data\Institut_Pascal.gdb
HostName\ c:\programs\interbase\data\Institut_Pascal.gdb www.jcolibri.com\ c:\programs\interbase\data\Institut_Pascal.gdb
- dans Params, les paramètres propres à Interbase. Dans notre cas, les paramètres par défaut du Serveur, plus le nom d'utilisateur et le mot de passe.
Une fois les propriétés initialisées, nous pouvons tester la connection en basculant tIbDatabase.Connected sur True. A titre de vérification: |
posez un second tIbDataBase sur la forme | | initialisez IbDatabase2.DatabaseName avec
..\data\Institut_Pascal.gdb | |
cliquez deux fois sur IbDatabase2 pour ouvrir l'éditeur de tIbDatabase | | Delphi présente le dialogue suivant:
| |
sélectionnez "User Name" et tapez SYSDBA | | sélectionnez "Password" et tapez masterkey |
| supprimez la coche de "Login Prompt" | | le résultat est:
| |
fermez l'éditeur | | sélectionnez Connected et basculez sa valeur sur True |
| au bout d'un "petit" instant, la valeur bascule sur True |
Notez que: - la connection est le test IMPERATIF pour vérifier que nous pouvons
travailler en Interbase. Nous recommandons d'effectuer cette connection systématiquement avant de poser des tonnes de composants sur la tForm ou d'écrire des milliers de lignes
- si nous connectons la base en mode conception, Delphi ouvrira aussi la base lorsque l'exécution sera lancée. Ces deux connections sont comptabilisés par le gestionnaire de licences Interbase comme 2 connections séparées (ce sont
bien deux processus Windows). Si vous travaillez en utilisant la version Delphi d'Interbase, il vaut mieux fermer la connection en mode conception, puis la rouvrir lors de l'exécution par:
tIbDatabase.Open; 4 - Créer une Table 4.1 - Principe Nous allons créer une table contenant pour chaque formation:
- un code (par exemple 8)
- un nom (par exemple "Delphi Interbase")
- un prix (par exemple 1.400)
Pour cela nous devons envoyer une requête en langage SQL vers le Serveur Interbase.
La syntaxe de cette requête est: CREATE TABLE formations
(f_numero INTEGER, f_nom CHARACTER(11), f_jours INTEGER, f_prix NUMERIC(5, 2) ) |
Il suffit donc de choisir un nom de table, et le nom de chaque colonne avec son type. Parmi les types autorisés par Interbase citons: - INTEGER pour les valeurs entières 32 bits
- SMALLINT pour les valeurs entières 16 bits
- NUMERIC(decimales, précision) pour une valeur numérique flottante
- DATE pour une date
- CHARACTER(taille) pour des caractères
Pour envoyer cette requête vers le Serveur: - nous utilisons un tIbDataBase qui assurera la connection vers le Serveur
- nous utilisons un tIbQuery
- nous le relions à tIbDatabase
- nous plaçons la requête SQL dans sa propriété IbQuery.SQL (via l'Inspecteur ou en code)
- nous appelons tIbQuery.ExecSql
- la création n'est visible par tout le monde que si la transaction qui est utilisée pour la création de la table est confirmée
4.2 - Utilisation de SQL
La requête à envoyer au Serveur est placée dans tIbQuery.Sql. tIbQuery.Sql est un descendant de tStrings. Nous pouvons donc utiliser, par exemple:
IbQuery1.Sql.Add('CREATE TABLE formations (f_numero INTEGER, f_nom CHARACTER(11))');
| Pour construire la requête: - nous pouvons utiliser Add pour ajouter une ligne de requête, Text pour affecter une requête, Clear pour purger tout texte antérieur, ou même
LoadFromFile pour lire un fichier .txt contenant la requête:
IbQuery1.Sql.LoadFromFile('cree_formation.txt'); |
La mise en page n'a aucune importance pour le Serveur: la requête peut être répartie en plusieurs lignes:
tIbQuery.Sql.Add('CREATE TABLE');
tIbQuery.Sql.Add(' formations ');
tIbQuery.Sql.Add(' (f_numero INTEGER, f_nom CHARACTER(11))');
|
l_requete:= 'CREATE TABLE '+ Edit1.Text+ ' (';
l_requete:= l_requete+ Edit2.Text+ ')';
tIbQuery.Sql.Add(l_requete); |
4.3 - Comment ça Marche Au niveau fonctionnement: Notez que l'envoi de toute requête qui modifie des données du Serveur (et la
création d'une nouvelle table est bien une modification) ne peut se faire que par du code (PAS en basculant IbQuery1.Active sur True en mode conception)
4.4 - Les Transactions
En Interbase, toutes les requêtes sont créées dans le cadre d'une transaction. Les transaction sont un mécanisme qui garantit que plusieurs sont réalisées en entier ou annulées. L'archétype est le transfert bancaire: si nous débitons
DUPOND pour créditer MARTIN, le système doit garantir que soit les deux modifications sont réalisée ou aucune ne l'est. En Delphi, les composants de bases de données utilisent par défaut des
transactions transparentes pour le programmeur, mais nous pouvons gérer les transactions nous-même. C'est ce qui est recommandé pour Interbase. En InterbaseExpress, nous utilisons un composant tIbTransaction que nous
connectons à tIbDatabase.DefaultTransaction. Les primitives sont: - tIbTransaction.StartTransaction pour démarrer une nouvelle transaction avant une ou plusieurs opérations
- tIbTransaction.Commit pour confirmer la suite d'opération, ou tIbTransaction.RollBack pour tout annuler.
- StartTransaction et Commit ou RollBack sont souvent utilisés dans TRY
EXCEPT:
TRY
IbTransaction1.StartTransaction;
débite DUPOND
crédite MARTIN
IbTransaction1.Commit;
EXCEPT
IbTransaction1.RollBack;
END; | Nous ne pouvons appeler IbTransaction1.StartTransaction si la transaction
attachée à IbTransaction1 est fermée (committed ou Rolledback). Pour savoir si une transaction est active, nous pouvons utiliser la fonction InTransaction. Ainsi:
IF NOT IbTransaction1.InTransaction
THEN IbTransaction1.StartTransaction; |
Que se passerait-il si nous réalisons le traitement sans cette IbTransaction: - pendant l'exécution de notre application d'autres applications (l'explorateur de bases de données, un autre de nos exe) ne "verraient" pas la table
- un autre IbQuery de notre propre application pourrait ne pas voir la table
- lorsque l'EXE sera fermé, la transaction sera automatiquement fermée
Le plus simple est donc d'utiliser Commit.
4.5 - L'application Pour créer notre table | créez une nouvelle application et appelez-la "ib_create_table" |
| placez un tIbDataBase sur la Forme. Cliquez deux fois sur IbDataBase1, renseignez "local", "DataBase", "User Name" "Pass Word" et "Login Prompt":
Vérifiez la connection en basculant IbDataBase.Connected sur True, puis fermez la connection (pour éviter de monopoliser un utilisateur). La
connection sera ouverte avant l'envoi de la requête | | placez un tIbTransaction sur la Forme, et reliez IbDatabase1.DefaultTransaction |
| placez un tIbQuery sur la tForme | |
sélectionnez sa propriété DataBaseName et initialisez-la à IbDataBase1 | |
placez un tButton sur la Forme et créez sa méthode OnClick. Placez-y les instructions de création: |
procedure TForm1.create_table_Click(Sender: TObject);
begin
with IbQuery1 do
begin Close;
with Sql do
begin Clear;
Add('CREATE TABLE formations');
Add(' ('
+' f_numero INTEGER,');
Add(' f_nom CHAR(9),');
Add(' )');
end; // with Sql
Try
IbDatabase1.Open;
if ibtransaction1.InTransaction
then ibTransaction1.Commit;
ibtransaction1.StartTransaction;
ExecSql;
ibtransaction1.Commit;
except
on e: Exception do
display('pb_create '+ e.Message);
end; // try ... except
end; // with IbQuery1
end; // create_table_Click |
| compilez, exécutez, et cliquez le bouton |
Vous pouvez télécharger le sources du projet "ib_create_table.zip".
4.6 - Vérifier la création Nous pouvons vérifier que la création a été effectuée en utilisant l'explorateur de bases de données Delphi, ou en lisant les données ce cette table dans notre application.
Pour lire les données il suffit d'envoyer la requête
au moteur. Nous verrons cette requête SELECT en détail plus bas, mais voici comment procéder pour notre test: |
ajoutez un second tIbQuery sur la tForme | | sélectionnez sa propriété DataBaseName et initialisez-la à
IbDataBase1 | | placez un second tButton sur la Forme et placez-y la requête de lecture: |
procedure TForm1.select_Click(Sender: TObject);
begin
with IbQuery2 do
begin Close;
with Sql do
begin Clear;
Add('SELECT * FROM formations');
Try Open;
display('ok');
except
On e: Exception do
display('not_there '+ e.Message);
end;
end; // with Sql
end; // with IbQuery2
end; // select_Click |
| compilez, exécutez, et cliquez le bouton |
4.7 - Effacer une table Pour supprimer une Table, il faut exécuter la requête:
Donc: |
ajoutez un troisième tIbQuery sur la tForme | | sélectionnez sa propriété DataBaseName et initialisez-la à
IbDataBase1 | | placez un autre tButton sur la Forme et placez-y la requête de suppression: |
procedure TForm1.drop_table_Click(Sender: TObject);
begin IbDatabase1.Open;
with IbQuery3 do
begin Close;
with Sql do
begin Clear;
Add('DROP TABLE formations');
end; Try
if ibtransaction1.InTransaction
then ibTransaction1.Commit;
ibtransaction1.StartTransaction;
ExecSql;
ibtransaction1.Commit;
display(' ok');
except
on e: exception do
display('pb_drop '+ e.Message);
end;
end; // with IbQuery3
end; // drop_table_Click |
| compilez, exécutez, et cliquez le bouton | Notez que: - nous avons utilisé 3 IbQuery séparés. Nous aurions aussi bien pu utiliser le même
- nous aurions même pu utiliser un IbSql. Pour ce tutorial que nous souhaitons minimal, nous avons préféré un IbQuery qui permet à la fois la
modification de données du Serveur (CREATE, DROP) et la lecture de données depuis le Serveur (SELECT)
- notez toutefois, que pour envoyer une requête de modification il faut utiliser tIbQuery.ExecSql, et pour la lecture c'est tIbQuery.Open.
tIbQuery.Open est équivalent à tIbQuery1.Active:= True (mais pas à ExecSql). Comme l'Inspecteur propose Active (pour pouvoir positionner ses
éléments visuels), nous pouvons ouvrir une Table depuis l'Inspecteur, mais nous ne pouvons pas créer de Table en mode conception (il faut exécuter du code)
4.8 - Automatisation de la création
Si nous avons plusieurs tables à créer, le codage en dur se révèle très vite fastidieux. Or il est très simple de paramétrer une procédure générique, et d'appeler cette procédure en lisant les paramètres depuis un fichier.
Une solution est de placer sur disque les requêtes SQL et de lire et exécuter ces requêtes les unes après les autres. Nous avons préféré utiliser une définition plus schématique qui est analysée par la procédure de lecture.
Voici notre schéma que nous avons tapé dans NotePad et placé dans le fichier "schema_simple.txt": formations
f_numero INTEGER f_nom CHAR(23) f_jours INTEGER
f_prix NUMERIC(12, 2) dates d_formation INTEGER
d_date DATE d_ville INTEGER villes
v_numero INTEGER v_nom CHAR(30) | Nous avons simplement fourni
- le nom de chaque table, à la marge
- le nom des champs et leur type Interbase, indenté de deux espaces
On peut difficilement faire plus simple ! Notre programme va alors - lire le fichier
- générer la requête pour créer chaque table
Et: - la création de la table est réalisée par une classe dont voici la définition:
type c_create_ib_table= class(c_basic_object)
m_table_name: String;
m_c_ib_query_ref: tIbQuery;
m_c_fields: tStringList;
m_exec_sql: Boolean;
Constructor create_ib_table(p_name, p_table_name: String;
p_c_ib_query: tIbQuery);
procedure drop_table;
procedure create_table;
procedure display_sql;
Destructor Destroy; Override;
end; // c_create_ib_table
| - la méthode de création de la table est la suivante:
procedure c_create_ib_table.create_table;
var l_field_index: Integer; begin
drop_table;
with m_c_ib_query_ref, Sql do
begin display(m_table_name);
Clear;
Add('CREATE TABLE '+ m_table_name+ ' (');
for l_field_index:= 0 to m_c_fields.Count- 2 do
Add(m_c_fields[l_field_index]+ ',');
Add(m_c_fields[m_c_fields.Count- 1]+ ' )');
if m_exec_sql
then try
if DataBase.DefaultTransaction.InTransaction
then DataBase.DefaultTransaction.Commit;
DataBase.DefaultTransaction.StartTransaction;
ExecSql;
DataBase.DefaultTransaction.Commit;
display(' ok '+ m_table_name);
except
on e: Exception do
display(' *** pb_create '+ e.Message);
end // try ... Except
else display('did_not_ask_to_create');
end; // with m_c_ib_query_ref, Sql
end; // create_table | - cette méthode utilise la liste de définition des champs qui est chargée à
partir du fichier par la procédure suivante
procedure TForm1.create_script_Click(Sender: TObject);
var l_list_index: Integer;
l_line, l_trimmed_line: String;
l_c_create_ib_table: c_create_ib_table;
l_table_index: Integer; begin
with tStringList.Create do
begin
LoadFromFile(k_script_path+ k_script_name);
l_c_create_ib_table:= Nil;
l_table_index:= 0;
for l_list_index:= 0 to Count- 1 do
begin
l_line:= Strings[l_list_index];
l_trimmed_line:= Trim(l_line);
if l_trimmed_line<> ''
then begin
if l_trimmed_line= l_line
then begin
if Assigned(l_c_create_ib_table)
then begin
l_c_create_ib_table.create_table;
l_c_create_ib_table.Free;
Inc(l_table_index);
end;
display_line;
l_c_create_ib_table:=
c_create_ib_table.create_ib_table('', l_line, IbQuery1);
end
else begin
l_c_create_ib_table.m_c_fields.Add(l_trimmed_line);
end;
end;
end; // for l_list_index
// -- the last table
if Assigned(l_c_create_ib_table)
then begin
l_c_create_ib_table.create_table;
l_c_create_ib_table.Free;
end; Free;
end; // with tStringList
end; // create_script_Click | Notez que:
- la procédure c_create_ib_table.create_table commence par appeler la procédure c_create_ib_table.drop_table. Pour la première création, ceci est inutile, mais si vous créez une seconde fois la base, il faut d'abord
effacer la table, sinon le Serveur refuse la création.
- mais dans le cas de la première création, DROP TABLE provoque aussi une exception. C'est pourquoi cette instruction est placée dans un
TRY ... EXCEPT. Cette exception va interrompre l'exécution. Pour poursuivre la création:
- vous pouvez taper F9 pour passer outre
- vous pouvez placer Delphi en mode "ne pas s'arrêter en cas d'exceptions"
en suprimant la coche de la CheckBox "stop on Delphi Exceptions" située dans:
Tools | Debugger | Language Exceptions C'est le mode préféré lorsque l'on utilise beaucoup de bases de données,
compte tenu des nombreuses exceptions provoquées à tous les niveaux (Serveur Sql, pilote, BDE ou dBExpress, Ibx etc).
Vous pouvez télécharger le source du projet
"ib_create_tables_with_script.zip".
5 - Ajouter des Données 5.1 - Ajout simple Ajoutons un enregistrement pour le stage
3, Interbase Delphi L'instruction SQL est: INSERT INTO formations
(f_numero, f_nom) VALUES (3, 'Interbase Delphi') |
L'ajout d'enregistrement va modifier les données du Serveur, donc nous utiliserons tIbQuery.ExecSql. De façon détaillée: |
créez une nouvelle application et nommez-la "ib_insert_data" | | placez un tIbTransaction sur la Forme |
| placez un tIbDataBase sur la Forme. Cliquez deux fois sur IbDataBase1, renseignez "local", "DataBase", "User Name" "Pass Word" et "Login Prompt".
Vérifiez la connection en basculant IbDataBase1.Connected sur True, puis fermez la connection. Sélectionnez DefaultTransaction et initialisez-la à IbTransaction1 |
| placez un tIbQuery sur la tForme | |
sélectionnez sa propriété DataBaseName et initialisez-la à IbDataBase1 | |
placez un tButton sur la Forme et créez sa méthode OnClick. Placez-y les instructions d'ajout:
procedure TForm1.insert_Click(Sender: TObject);
begin IbDatabase1.Open;
with IbQuery1 do
begin Close;
with Sql do
begin Clear;
Add('INSERT INTO formations');
Add(' (f_numero, f_nom)');
Add(' VALUES (8, ''Delphi InterBase'')');
end; // with Sql
Try
if not IbTransaction1.InTransaction
then IbTransaction1.StartTransaction;
ExecSql;
IbTransaction1.Commit;
except
on e: Exception do
display('pb_insert '+ e.Message);
end; // Try...Except
end; // with IbQuery1
end; // insert_Click | |
| compilez, exécutez, et cliquez le bouton |
Vous pouvez télécharger les sources du projet "ib_insert_data.zip".
5.2 - Type CHARACTER Pour spécifier les valeurs CHARACTER, Sql exige que la chaîne soit entourée de guillemets. Suivant les Serveurs, il faut utiliser un guillemet simple ou double:
VALUES (3, 'Interbase Delphi') | ou
VALUES (3, "Interbase Delphi") |
De plus si notre valeur est nichée dans une String Pascal, il faut dédoubler les guillemets
IbQuery1.Sql.Add(' VALUES (3, ''Interbase Delphi'')'); |
Pour simplifier cet imbroglio de guillemets, Delphi propose la méthode QuotedStr:
IbQuery1.Sql.Add(' VALUES (3, '+ QuotedStr('Interbase')+ ')');
|
5.3 - Type NUMERIC Pour les valeurs numériques avec décimales, nous devons batailler avec les points et les virgules: La règle est donc simple: - les valeurs Delphi (Double ou autre) utilisent le point '.'
- les composants visuels (tEdit etc) et les primitives de conversion
(FloatToStr) utilisent la virgule ','
5.4 - Type DATE Un problème similaire intervient pour les dates: En supposant que nous souhaitions fournir la date du 29 Mars 2004, nous pouvons utiliser:
INSERT INTO dates (d_numero, d_date) VALUES (3, '2004/03/29') | et:
IbQuery1.Sql.Add('INSERT INTO dates');
IbQuery1.Sql.Add(' (d_numero, d_date');
IbQuery1.Sql.Add(' VALUES (3, ''2004/03/29'')'); |
5.5 - Automatisation de l'Ajout Les valeurs à insérer ont été figées dans notre code. Nous pouvons automatiser cet ajout - soit par des scripts
- soit par une procédure amplement paramétrée
- soit en mode interactif.
Nous allons présenter ici l'utilisation d'une procédure. En fait il n'y a rien de nouveau par rapport à la technique ci-dessus, sauf que - la chaîne de la requête est construite en fonction de paramètres de la procédure
- la procédure appelante envoie les paramètres requis
Voici un exemple de procédure générique d'ajout:
procedure insert_generic(p_number: Integer; p_name: String; p_days: Integer; p_cost: Double);
begin
with Form1, IbQuery1 do
begin Close;
with Sql do
begin Clear;
Add('INSERT INTO formations');
Add(' (f_numero, f_nom, f_jours, f_prix)');
DecimalSeparator:= '.';
Add(' VALUES ('+ IntToStr(p_number)+ ', '+ QuotedStr(p_name)
+ ', '+ IntToStr(p_days)+ ', '+ FloatToStr(p_cost)+ ')');
DecimalSeparator:= ',';
end; Try
if not IbTransaction1.InTransaction
then IbTransaction1.StartTransaction;
ExecSql;
IbTransaction1.Commit;
display(' ok');
except
on e: Exception do
display(' pb_insert '+ e.Message);
end;
end; // with IbQuery1
end; // insert_generic | Et voici un exemple de procédure appelante
procedure TForm1.insert_batch_Click(Sender: TObject);
begin IbDatabase1.Open;
insert_generic(1, 'Initiation Delphi', 3, 1400.40);
insert_generic(2, 'Bases de Données Delphi', 3, 1.400);
insert_generic(3, 'Interbase Delphi', 3, 1.400);
insert_generic(4, 'Composants Delphi', 3, 1.400);
insert_generic(5, 'UML Delphi', 3, 1.400);
insert_generic(4, 'Initiation Pascal', 4, 1.900);
end; // insert_batch_Click | Notez que:
- la procédure appelante pourrait aussi bien lire ses donnée d'une autre source (un fichier FILE OF, un fichier ASCII ("comma separates values" ou autre), un autre table (Oracle, Sql Serveur ou même une autre table Interbase...)
- le paramètres p_cost est de type Double:
- la procédure appelante envoie une valeur littérale avec un point décimal
insert_generic(2, 'Bases de Données Delphi', 3, 1.400); |
- la procédure appelée utilise FloatToStr, qui attend une virgule décimale. Nous forçons donc temporairement l'utilisation du point:
DecimalSeparator:= '.';
Add(' VALUES ('+ IntToStr(p_number)+ ... + FloatToStr(p_cost)+ ')');
DecimalSeparator:= ','; |
6 - Lire et Afficher 6.1 - 5.1- Principe Pour afficher un enregistrement, nous devons d'abord récupérer ses valeurs du Serveur. Pour lire les données contenues dans une table, SQL utilise l'instruction
SELECT. Par exemple: SELECT f_numero, f_nom
FROM formations | Lorsque le Serveur reçoit cette requête: - il vérifie sa syntaxe
- il construit une table contenant les valeurs demandées
- ces données sont envoyées au Client
Plus concrètement:
Vous pouvez télécharger le source du projet "ib_select.zip".
6.2 - Comment ça Marche
Lorsque nous exécutons IbQuery1.Open: - Delphi envoie la requête SELECT au Serveur
- celui-ci construit une table résultat, en utilisant, bien sûr, la table
FORMATIONS, mais aussi éventuellement des tables annexes (index, contraintes etc.).
- cette table résultat est envoyée via le réseau au Client Interbase. Celui
ci la transmet ensuite à l'application. Delphi place alors ce résultat dans un tampon mémoire associé à tIbQuery:
6.3 - Affichage
Une fois que IbQuery a récupéré les données nous pouvons les afficher ou les traiter en mémoire. Pour afficher les données nous utilisons: - un tDataSource qui récupère les données du tIbQuery
- un ou plusieurs composants d'affichage relié au tDataSource. Par exemple un tDbGrid.
Par conséquent: |
sélectionnez dans la page "Data Access" de la Palette le composant tDataSource: et placez le sur la Forme |
| sélectionnez dans la page "Data Controls" de la Palette le composant tdbGrid: et placez le sur la Forme
| | sélectionnez sa propriété DataSource et donnez-lui la valeur DataSource1 |
| compilez, exécutez, et cliquez le bouton qui lance la requête |
6.4 - Affichage en mode conception
Nous pouvons aussi lancer la requête de lecture en mode conception. Il faut pour cela que: - tIbDatabase soit correctement initialisé
- tIbQuery soit relié à tIbDatabase
- la propriété tIbQuery.Sql soit initialisée en utilisant l'Inspecteur
- la requête soit envoyée en basculant tIbQuery.Active sur True
Donc:
6.5 - SELECT
SELECT est la seule instruction de lecture de données du Serveur. Sa structure générale est:
SELECT colonnes FROM tables WHERE conditions |
et: - colonnes indique quelles colonnes nous voulons voir figurer dans le résultat. Nous pouvons
- citer explicitement les colonnes souhaitées, dans l'ordre qui nous convient:
SELECT f_nom, f_numero FROM formations |
Nous pouvons ne demander qu'une partie des colonnes (projection) SELECT f_nom
FROM formations | L'abréviation "*" permet de désigner "toutes les colonnes"
- effectuer des sommes, des moyennes (agrégats)
SELECT COUNT(*)
FROM formations | le résultat est alors une table d'une ligne, une colonne avec le nombre de formations
- tables contient le noms des tables à utiliser pour calculer le résultat. Par exemple:
SELECT *
FROM formations, dates WHERE f_nom='UML ET DELPHI' |
- conditions permet
- de ne retenir que certaines lignes du résultat. Par exemple
SELECT *
FROM dates WHERE f_date> '2004/05/01' | - de trier le résultat:
SELECT * FROM dates
ORDER BY f_date | Ce qu'il faut bien comprendre est que:
- SELECT calcule un résultat A PARTIR de tables, mais que le résultat n'EST PAS les tables. Lorsque nous demandons une somme ou une moyenne, par exemple, SELECT retourne un nombre.
- Certes, "SELECT * FROM formations" retourne bien les mêmes données que celle de la table sur disque "formations". Mais ce n'est qu'un
instantané réalisé lorsque la requête a été lancée. Il s'agit d'une copie. Si un autre utilisateur ajoute une nouvelle formation la copie que nous avons dans le cache attaché à IbQuery ne le reflète pas
- pour rafraîchir notre cache, la SEULE façon de faire est de relancer la requête (en fermant et réouvrant tIbQuery)
6.6 - Changement de requête
Notre exemple présente une lecture dans laquelle l'utilisateur tape sa requête dans un tEdit. Rien de bien particulier, sauf que ceci permet d'explorer facilement une table (sans utiliser l'Explorateur de bases de données).
6.7 - Requête paramétrée Chaque fois que nous souhaitons une information, nous devons exécuter un SELECT. Lorsque la requête est complexe (plusieurs tables, sous-requête, agrégats etc)
le Serveur essaye d'optimiser le calcul du résultat. Cette optimisation peut être longue. Si nous devons exécuter la requête plusieurs fois de suite, Sql offre la possibilité de scinder la requête en deux:
- envoyer le squelette de la requête contenant des paramètres
- fournir la valeur des paramètres et demander le résultat
Dans le premier temps, le Serveur calcule sa méthode d'accès, mais n'exécute
rien car il lui manque la valeur des paramètres. Lorsque le Serveur reçoit les paramètres, il lance le calcul en utilisant son plan d'évaluation, et retourne le résultat. Et ceci autant de fois que le squelette de la requête ne change pas.
Pour effectuer ce travail en deux temps: - nous rédigeons une requête qui contient des paramètres. En Delphi, ces paramètres sont désignés par un identificateur précédé de ":". Par exemple
SELECT * FROM dates
WHERE f_date= : ma_date | Notez que:
- l'identificateur n'a pas besoin d'avoir le même nom que la colonne
- les paramètres peuvent seulement apparaître dans la clause WHERE (pas dans les champs ou pour le nom des tables)
- ":" et ma_date sont collés (pas d'espace entre les deux)
- la requête est envoyée vers le Serveur par
- lorsque nous souhaitons obtenir un résultat
- nous initialisons la valeur des paramètres en utilisant tIbQuery.Params
IbQuery1.Params[0].AsString:= '2004/03/01'; |
ou
IbQuery1.ParamByName('ma_date').AsString:= Edit3.Text;
| - nous lançons la requête par:
En pratique:
| créez une nouvelle application et appelez-la "p_ib_parametrized" | |
placez un tIbTransaction sur la Forme | | placez un tIbDataBase sur la Forme. Cliquez deux fois sur IbDataBase1,
renseignez "local", "DataBase", "User Name" "Pass Word" et "Login Prompt". Vérifiez la connection en basculant IbDataBase1.Connected sur True, puis fermez la connection.
Sélectionnez DefaultTransaction et initialisez-la à IbTransaction1 | | placez un tIbQuery sur la tForme |
| sélectionnez sa propriété DataBaseName et initialisez-la à IbDataBase1 | |
placez un tButton sur la Forme et créez sa méthode OnClick. Placez-y les instructions de préparation:
procedure TForm1.prepare_Click(Sender: TObject);
begin
with IbQuery1 do
begin Close;
with Sql do
begin Clear;
Add('SELECT * FROM formations WHERE f_nom= :le_nom');
end; // with Sql
Prepare; end; // with IbQuery1
end; // prepare_Click | |
| placez un tButton sur la Forme et créez sa méthode OnClick. Placez-y les instructions qui initialisent les paramètres et ouvrent la table:
procedure TForm1.close_params_open_Click(Sender: TObject);
begin
with IbQuery1 do
begin
// -- close in case has done a previous request
Close; // -- set the parameter values
with Sql do
ParamByName('le_nom').AsString:= parameter_edit_.Text;
// -- send the parameter to the database
Open; end; // with IbQuery1
end; // close_params_open_Click | |
| compilez, exécutez, et cliquez le bouton qui lance la requête |
Vous pouvez télécharger le source du projet "ib_parametrized.zip".
7 - Modifier des Données 7.1 - Principe
Supposons que notre table FORMATIONS contienne les valeurs suivantes: Pour changer le libellé "Interbase Delphi" en "Interbase" nous utilisons une requête UPDATE:
UPDATE formations SET f_nom= 'Interbase'
WHERE f_numero= 8 | Cette requête qui modifie les données du Serveur est envoyée vers le Serveur en utilisant tIbQuery1.ExecSql
En détail:
Vous pouvez télécharge le source du projet ib_update_data.zip".
7.2 - La clause WHERE
Dans l'instruction précédente nous avons spécifié la valeur f_numero à l'aide de WHERE. Que se passerait-il si nous envoyons:
UPDATE formations SET f_nom= 'Interbase' |
Eh bien le Serveur modifierait le nom de TOUS les enregistrements. Si nous souhaitons limiter les modifications à certaines lignes, il est impératif: - que chaque ligne de chaque table ait un identificateur unique pour que nous
puissions désigner cette ligne en cas de modification
- que nous indiquions dans UPDATE les lignes à modifier
Si en revanche nous souhaitons modifier plusieurs lignes à la fois, nous pouvons :
- omettre WHERE. Par exemple, pour passer tous les noms en majuscule:
UPDATE formations SET f_nom= UPPER(f_nom) |
- changer plusieurs lignes satisfaisant une condition. Pour effectuer une réduction promotionnelle de 5% sur les prix inférieurs à 1400 euros :
UPDATE formations SET f_prix= f_prix* 0.95
WHERE WHERE f_prix<= 1400.00 | Toutefois: - ne répétez pas cette requête de réduction trop souvent: il faut bien que je mange un peu !
- nous faisons des conditions tarifaires particulières (groupes, formations intra, étudiant, particulier, réinsertion...), que nous sommes prêts à vous présenter si vous me téléphonez directement à l'Institut Pascal au 01.42.83.69.36.
Il est donc fondamental de bien comprendre qu'un UPDATE sans clause WHERE porte sur tous les enregistrements.
7.3 - UPDATE et AFFICHAGE
Ce fonctionnement de UPDATE est particulièrement important lorsque nous modifions un enregistrement affiché à l'écran dans un dbGrid: nous pensons que
Interbase sait que nous "modifions l'enregistrement courant". Or ce concept d'enregistrement courant n'existe pas en mode SQL:
La seule façon de modifier une valeur affichée de façon interactive est de déclencher une requête UPDATE en utilisant un composant tIbUpdateSql. Le
plus simple pour synchroniser les modifications est d'utiliser un composant tIbUpdateSql.
7.4 - tIbUpdateSql Ce composant a pour vocation de contenir la requête SQL qui doit être exécutée
lorsque le tIbQuery doit être modifié. Cette requête est placée dans tIbUpdaateSql.ModifySql. Automatisons par exemple la mise à jour de f_nom lorsque nous tapons une valeur dans un tDbGrid:
| créez une nouvelle application et appelez-la "p_update_sql" | |
placez un tIbTransaction sur la Forme | | placez un tIbDataBase sur la Forme. Cliquez deux fois sur IbDataBase1,
renseignez "local", "DataBase", "User Name" "Pass Word" et "Login Prompt". Vérifiez la connection en basculant IbDataBase1.Connected sur True, puis fermez la connection.
Sélectionnez DefaultTransaction et initialisez-la à IbTransaction1 | | placez un tIbQuery sur la tForme
- sélectionnez sa propriété DataBaseName et initialisez-la à IbDataBase1
- sélectionnez sa propriété Sql et placez-y la requête
|
| ajoutez un composant tDataSource - sélectionnez DataSet et donnez-lui la valeur IbQuery1
|
| reliez DataSource1 à un tDbGrid: - placez un tdbGrid de sur la Forme
- sélectionnez sa propriété DataSource et reliez la à DataSource1
| |
ouvrez IbQuery1 en basculant Active sur True | | ajoutez un tIbUpdateSql:
- sélectionnez la page "Interbase" de la Palette, sélectionnez le composant tIbUpdateSql
et posez-le sur la Forme
- connectez le IbQuery1 et IbUpdateSql:
- en initialisant IbQuery1.UpdateObject avec IbUpdateSql1
- en basculant IbQuery1.CachedUpdate à True
- sélectionnez sa propriété IbUpdateSql1.ModifySql, cliquez sur l'ellipse pour ouvrir l'éditeur de ModifySql (c'est un éditeur de tStrings classique):
- tapez-y la requête de modification
UPDATE formations
SET f_nom= :f_nom WHERE f_numero= :f_numero | |
| compilez, exécutez: - Sélectionnez dbGrid1, tapez une valeur dans la colonne F_NOM.
- Notez bien que le dbGrid permet à présent la modification de valeurs (alors que sans tIbUpdateSql ces modifications étaient impossibles).
- lorsque nous quittons la ligne (en changeant de composant, ou en
changeant de ligne dans le dbGrid), la requête de modification est envoyée vers le Serveur
|
Notez que: - pour désigner les valeur des champs à utiliser, nous avons employé une
requête paramétrée, en désignant les paramètres par le nom du champ:
- nous aurions pu modifier plusieurs colonnes
UPDATE formations
SET f_nom= :f_nom, f_jours:= :f_jour, f_prix= :f_prix WHERE f_numero= :f_numero
|
Vous pouvez télécharger le projet "ib_update_sql.zip".
7.5 - "Old" values
Nous avons utilisé f_numero pour identifier la ligne à modifier. Mais que se passe-t-il si nous souhaitons modifier la clé elle-même ? En effet :f_numero désigne la valeur actuelle, qui est la valeur modifiée, et non pas la valeur
qui permet de désigner la ligne dans la table. Pour résoudre ce problème, Delphi a crée le concept des "old_values", correspondant à la valeur que le champ avait lors de la lecture de la table. Et
le paramètre pour désigner la valeur se note :old_f_numero Si nous souhaitons pouvoir modifier tous les enregistrements, nous utilisons donc dans tIbUpdateSql.ModifySql une requête du type:
UPDATE formations
SET f_numero= :f_numero, f_nom= :f_nom WHERE f_numero= :old_f_numero |
Ce concept de "old values" est fondamental pour la mise à jour concurrente que nous présenterons voir ci-dessous.
7.6 - Insertion, Effacement Notez que:
- tIbUpdateSql possède aussi des propriétés InsertSql et DeleteSql qui peuvent contenir les instructions à éxécuter lorsque nous ajoutons un nouvel enregistrement (en ajoutant une ligne au bas du tDbGrid), ou que nous en
effaçons un (par Ctlr Suppr sur une ligne du tDbGrid)
- le composant tIbUpdateSql possède un éditeur de composant permettant un écriture plus automatique des requêtes SQL. Cliquez deux fois sur
IbUpdateSql1 et Delphi présentera:
8 - Effacer de Données 8.1 - Principe de l'effacement
Pour effacer l'enregistrement ayant le numéro 8, nous exécutons une requête DELETE
FROM formations WHERE f_numero= 3 | En détail:
- créez une nouvelle application "p_delete_data"
- placez un tIbTransaction sur la Forme
- placez un tIbDataBase sur la Forme. Cliquez deux fois sur IbDataBase1,
renseignez "local", "DataBase", "User Name" "Pass Word" et "Login Prompt".
Vérifiez la connection en basculant IbDataBase1.Connected sur True, puis fermez la connection.
Sélectionnez DefaultTransaction et initialisez-la à IbTransaction1 - placez un tIbQuery sur la tForme
- sélectionnez sa propriété DataBaseName et initialisez-la à IbDataBase1
- placez un tButton sur la Forme et créez sa méthode OnClick. Placez-y les instructions de modification:
procedure TForm1.delete_edit_Click(Sender: TObject);
begin
with IbQuery1 do
begin Close;
with Sql do
begin Clear;
Add(DELETE FROM formations WHERE f_numero= 3);
end; // with Sql
Try ExecSql;
except
display('< *** pb_update');
end; // try...except
end; // with IbQuery1
end; // update_edit_Click | - compilez, exécutez, et cliquez le bouton
Vous pouvez télécharger le source du projet ib_delete_data.zip".
De façon naturelle: - comme pour UPDATE, il faut veiller à spécifier les enregistrements à
effacer dans WHERE. Une requête sans WHERE peut effacer toutes les lignes. Et sans aucun "undelete". A bon entendeur...
- nous pouvons utiliser un tIbUpdateSql en plaçant dans
tIbUpdateSql.DeleteSql la requête du type:
DELETE
FROM formations WHERE f_numero= :old_f_numero |
9 - Plusieurs Tables 9.1 - La Normalisation des Tables Lorsque nous créons une application, nous répartissons les données dans des tables: les factures, les articles, les adresses des fournisseurs etc.
Cette activité de répartition peut devenir très complexe. Imaginez la modélisation de l'activité de Renault ou des Galeries Farfouillettes: faut-il placer l'adresse des fournisseurs avec les pièces du stock, ou créer un table
séparée, combien de tables faut-il alors créer etc. Pour des projets plus modestes, la répartition est souvent intuitive, et se résume souvent à adapter des structures utilisées dans d'autres projets.
Néanmoins, les données utilisées par SQL doivent obéir à un nombre minimal de contraintes: - nombre de colonnes
- redondance / dépendances
Prenons le cas simple de formations dont le calendrier est le suivant (le vrai
calendrier se trouve à <%dates_des_formations>): |
Mars | Avril | Mai | Initiation Delphi: | 2 au 4 - Metz | | |
Delphi Interbase: | | 6 au 8 - Paris | 4 au 6 - Toulouse | Delphi UML: | 23 au 25 - Paris | 20 au 22 - Châlons
| 25 au 27 - Lyon | | 9.1.1 - Même nombre de colonnes La première exigence est simple: chaque enregistrement de chaque table
relationnelle doivent avoir le même nombre de colonnes. Dans notre cas, toutes les formations n'ont pas nécessairement le même nombre de sessions. La première exigence SQL n'est pas respectée: le premier
enregistrement aurait 2 colonnes, le second 3 etc. La solution est simple: il suffit de créer un enregistrement séparé pour chaque date.
9.1.2 - Non redondance - Anomalie de mise à jour Supposons que nous souhaitions modifier le nom "Interbase Delphi" en "Delphi Interbase". Il faudra veiller à modifier tous les enregistrements contenant
cette valeur. Le risque est donc des modifications partielles ou incohérente (anomalie de mise à jour). Le plus simple est alors de placer les valeurs mentionnées à plusieurs endroits
dans une table séparée avec un code, et d'utiliser ce code pour référencer la valeur commune dans les autres tables. Dans notre cas: - nous créons une table avec un code (arbitraire) de formation et le nom de la formation:
- nous créons une table avec les dates et les villes, en plaçant le code de la formation pour désigner le nom des formations
9.1.3 - Anomalie de suppression D'autre part, si le stage de Mars a eu lieu, nous effaçons la ligne "1, 2004/03/02, Metz". Ceci supprime aussi le fait que nous réalisons des stages à Metz.
Le fait que la suppression d'une ligne fasse disparaître une information est appelé "anomalie de suppression". Si nous souhaitons présenter les villes où les stages peuvent avoir lieu,
indépendamment de sessions programmées dans les trois prochains mois, il faut séparer les villes dans une table différente: - une table avec les villes possibles:
- une table avec les dates et le lieu:
D'où la configuration finale: - les types de formations:
- les villes où les sessions peuvent avoir lieu
- les types de stage, dates et le lieu:
9.2 - Jointure
Ayant réparti les données dans plusieurs tables pour répondre aux exigences SQL et au souci de non-redondance / absence d'anomalies, nous avons à présent l'obligation de recoller les morceaux pour reconstituer l'information de
départ: pour savoir où et quand se déroulent les formations "Interbase" nous devons: - cherche le code de la formation "Interbase" dans la table "formations": c'est le code 3
- trouver les dates et lieux dans la table "dates": (3, '2004/04/06', 101) et (3, '2004/05/04', 102)
- finalement le nom de la ville est trouvé dans la table "ville": 101 c'est Paris et 102 Toulouse
La solution standard proposée par SQL est la jointure: nous effectuons un SELECT en demandant de reconstituer une table en ajoutant les colonnes provenant de plusieurs tables:
Notez toutefois que:
- le résultat n'est pas très satisfaisant: le nom du stage est répété à chaque ligne
- le calcul est de plus inefficace: le Serveur ne peut nous retourner le résultat que lorsque tout le calcul est réalisé.
L'exemple de la jointure peut être téléchargé ici "ib_join.zip".
9.3 - Relation Maître Détail Pour éviter le calcul massif de la jointure, Delphi propose d'utiliser plutôt
le mécanisme Maître / Détail. Le principe est le suivant: - nous sélectionnons un enregistrement de la table Maître "formations"
- cet enregistrement contient le numéro de formation, qui permet de retrouver
dans la table Détail "dates" les dates pour ces formations
- il suffit alors de lancer un SELECT sur ces dates, pour récupérer les dates de ces formations
9.3.1 - Maître Détail manuel
Nous pouvons provoquer le calcul Maître Détail chaque fois que nous détectons une modification d'enregistrement dans la table Maître: c'est l'événement DataSourceMaître.OnDataChange qui nous signale ce changement. Par conséquent:
Par conséquent: | créez une nouvelle application et appelez-la "p_master_detail" |
| placez un tIbTransaction sur la Forme | |
placez un tIbDataBase sur la Forme. Cliquez deux fois sur IbDataBase1, renseignez "local", "DataBase", "User Name" "Pass Word" et "Login Prompt". Vérifiez la connection en basculant IbDataBase1.Connected sur True, puis
fermez la connection. Sélectionnez DefaultTransaction et initialisez-la à IbTransaction1 | | pour le Maître:
- placez un tIbQuery sur la tForme
- sélectionnez sa propriété DataBaseName et initialisez-la à IbDataBase1
- sélectionnez sa propriété Sql, ouvrez l'éditeur, et tapez:
SELECT f_numero, f_nom FROM formations |
- sélectionnez Active et basculez sa valeur sur True
- placez un tDataSource sur la Forme
- sélectionnez DataSet et initialisez sa valeur à IbQuery1
- placez un tdbGrid sur la Forme
- sélectionnez DataSource et initialisez sa valeur à DataSource1
- les données de la table "formation" apparaissent
|
| pour le Détail: - placez un second tIbQuery sur la tForme
- sélectionnez sa propriété DataBaseName et initialisez-la à IbDataBase1
- placez un tDataSource sur la Forme
- sélectionnez DataSet et initialisez sa valeur à IbQuery2
- placez un tdbGrid sur la Forme
- sélectionnez DataSource et initialisez sa valeur à DataSource2
| | provoquez la sélection de Détail en fonction du Maître
- sélectionnez DataSource1 (celui du MAITRE)
- créez son événement OnDataChange, et tapez-y la requête
procedure TForm1.DataSource1DataChange(Sender: TObject; Field: TField);
// -- when the MASTER datasource changes, select the details
begin
with IbQuery2 do
begin Close;
with Sql do
begin Clear;
Add('SELECT d_formation, d_date'
+ ' FROM dates'
+ ' WHERE d_formation= '+ IbQuery1.FieldByName('f_numero').AsString);
end; // with Sql
Open; end; // with IbQuery1
end; // TForm1.DataSource1DataChange |
- petite précaution: lorsque la Forme sera créée, IbQuery1 sera ouverte, les lignes remplies, et DataSource1.OnDataChange appelé. Il est donc impératif que IbQuery2 existe AVANT IbQuery1. Pour vous assurez que
c'est le cas, sélectionnez dans le menu "Edit" le sous-menu "Creation Order" et déplacez les composants pour que cette contrainte soit respectée:
|
| compilez, exécutez, et cliquez le bouton |
Vous pouvez télécharger ce projet "ib_master_detail.zip".
9.3.2 - Utilisation de tQuery.DataSource Nous pouvons demander à Delphi de réaliser le SELECT du détail automatiquement en initialisant la propriété tIbQueryDetail.DataSource du
Détail vers DataSourceMaître: | créez une nouvelle application et appelez-la "p_master_detail_automatic" |
| placez un tIbTransaction sur la Forme | |
placez un tIbDataBase sur la Forme. Cliquez deux fois sur IbDataBase1, renseignez "local", "DataBase", "User Name" "Pass Word" et "Login Prompt". Vérifiez la connection en basculant IbDataBase1.Connected sur True, puis
fermez la connection. Sélectionnez DefaultTransaction et initialisez-la à IbTransaction1 | | pour le Maître:
- placez un tIbQuery sur la tForme
- sélectionnez sa propriété DataBaseName et initialisez-la à IbDataBase1
- sélectionnez sa propriété Sql, ouvrez l'éditeur, et tapez:
SELECT f_numero, f_nom FROM formations |
- sélectionnez Active et basculez sa valeur sur True
- placez un tDataSource sur la Forme
- sélectionnez DataSet et initialisez sa valeur à IbQuery1
- placez un tdbGrid sur la Forme
- sélectionnez DataSource et initialisez sa valeur à DataSource1
- les données de la table "formation" apparaissent
|
| pour le Détail: - placez un second tIbQuery sur la tForme
- sélectionnez sa propriété DataBaseName et initialisez-la à IbDataBase1
- placez un tDataSource sur la Forme
- sélectionnez DataSet et initialisez sa valeur à IbQuery2
- placez un tdbGrid sur la Forme
- sélectionnez DataSource et initialisez sa valeur à DataSource2
| | provoquez la sélection de Détail en fonction du Maître
- sélectionnez IbQuery2 (le DETAIL)
- sélectionnez sa propriété DataSource et initialisez la à DataSource1 (le tDatasource du MAITRE)
- sélectionnez sa propriété Sql, ouvrez l'éditeur, et tapez y la requête de sélection:
SELECT d_formation, d_date FROM dates
WHERE d_formation= :f_numero | - sélectionnez Active et basculez sa valeur sur True
- vous verrez les deux table, et en mode conception
|
Notez que:
10 - Télécharger les Exemples Nous avons placé tous les projets dans des .ZIP qui comprennent: - le .DPR, la forme principale, les formes annexes eventuelles
- les fichiers de paramètres (le schéma et le batch de création)
- dans chaque .ZIP, toutes les librairies nécessaires à chaque projet (chaque .ZIP est autonaume)
Ces .ZIP contiennent des chemins RELATIFS. Par conséquent:
- créez un répertoire n'importe où sur votre machine
- placez le .ZIP dans ce répertoire
- dézippez et les sous-répertoires nécessaires seront créés
- compilez et exécutez
Ces .ZIP ne modifient pas votre PC (pas de changement de la Base de Registre, de DLL ou autre). Pour supprimer le projet, effacez le répertoire. Voici les .ZIP:
Comme d'habitude: - nous vous remercions de nous signaler toute erreur, inexactitude ou
problème de téléchargement en envoyant un e-mail à jcolibri@jcolibri.com. Les corrections qui en résulteront pourront aider les prochains lecteurs
- tous vos commentaires, remarques, questions, critiques, suggestion d'article, ou mentions d'autres sources sur le même sujet seront de même les bienvenus à jcolibri@jcolibri.com.
- plus simplement, vous pouvez taper (anonymement ou en fournissant votre e-mail pour une réponse) vos commentaires ci-dessus et nous les envoyer en cliquant "envoyer" :
- et si vous avez apprécié cet article, faites connaître notre site, ajoutez un lien dans vos listes de liens ou citez-nous dans vos
blogs ou réponses sur les messageries. C'est très simple: plus nous aurons de visiteurs et de références Google, plus nous écrirons d'articles.
11 - Conclusion
Lors de ce tutorial Interbase, nous avons présenté comment installer, créer une base, des tables, ajouter, lire, modifier et effacer des données. Si vous avez des questions, vous pouvez:
- vous reporter au livre Delphi dBase qui présente, par le détail, la gestion des composants visuels et les techniques
de traitement des données (formatage, vérfications, calculs...)
- pour les blobs, voyez Blobs Interbase
- vous pouvez aussi m'adresser vos questions par e-mail à jcolibri@jcolibri.com
Vous pouvez aussi consulter sur ce site d'autres articles
concernant Interbase: - le traitement multi-niveau qui utilise les couches dbExpress / DataSnap plutôt qu' Interbase Express (IBX): l'article
Interbase dbExpress vous présente ces techniques, ainsi que des schémas faisant comprendre la différence entre les architectures API, BDE, IBX, dbExpress.
- la présentation de l'architecture du moteur, analysée à partir de la version Open Source de Borland
- la présentation des versions d'Interbase disponibles sous C# (VCL.NET, WINDOWS FORMS) de Delphi 8:
- voyez les présentations à Borcon 2004
- Interbase dbExpresss: le mode dbExpress (Delphi 6, dbExpress). Le mode qui permet le mieux de comprendre
l'architecture Ado.Net qui en est directement issue
- Interbase Ibx.Net: le portage sous .Net de
la mécanique Ibx (Delphi 8, Ibx.Net). Le moyen le plus simple d'utiliser Interbase et Delphi 8. Contient aussi un comparatif de toutes ces architectures avec les schémas correspondants
- Interbase dbExpress.Net: le portage sous .Net de la mécanique dbExpress (Delphi 8, dbExpress.Net).
L'utilisation des techniques VCL pour Interbase ET pour les autres serveurs (Oracle, Sql Server, MyBase etc)
- Delphi 8 Ado.Net: sous Windows Forms, en l'absence de couche spécifique à Interbase nous pouvons utiliser une
mécanique similaire à Odbc et proche d'Ado sous Win32. Cet article présente ce fonctionnement
- Interbase Borland Data
Provider: la voie royale sous Windows Forms, et pour toutes les base, même pour tous les langages
- et pour le développement avec Delphi 2005 en ADO.Net, nous avons un
tutorial complet (similaire à celui-ci, mais en ADO.Net):
- firebird_ado_net_tutorial: ADO .Net Tutorial, utilsant Firebird.
Une introduction complète au développement ADO .Net, utilisant SqlConnection, SqlCommand, SqlDataAdapter pour se connecter à un Serveur, exécuter directement du SQL, travailler avec des tables en
mémoire, utiliser des DataGrids pour afficher et modifier les données. Très nombreux schémas et code source complets
avec une Formation Delphi 2005 .Net où 2 jours sont entièrement consacrés à ADO.Net.
Nous n'avons pas essayé d'être exhaustif dans la présentation. Lors des stages que j'anime personnellement à l'Institut Pascal, j'entre beaucoup plus dans le détail: - la gestion des multiples erreurs et exceptions qui interviennent lors
d'applications utilisant des bases de données
- les possibilités d'installer Interbase depuis Delphi, et d'interroger les paramètres de la Base ou du schéma depuis un programme Delphi, de lister les tables, les champs etc
- le traitement des contraintes (clé primaires, intégrité référentielle, intégrité sémantique)
- la manipulation détaillée des valeurs extraite d'une Table, en utilisant les
tFields et les différents composants visuels tDb_xxx, ainsi que les traitements spécifiques aux chaînes, aux dates, aux blobs
- les techniques de validation de saisie essentielles pour toute application de gestion
- l'étude approfondie du langage SQL, qui est au coeur de tout traitement avec des Serveurs Sql
- les triggers, les procédures cataloguées (tStoredProc), les générateurs
- la gestion d'utilisateurs multiples, avec les paramétrages des modes d'isolation des transaction Interbase
- la gestion de Serveur (sauvegarde / restauration, statistiques, réglage)
Pour examiner le programme détaillé de cette formation, cliquez Client Serveur Interbase. Nous
proposons de plus d'autres formations présentées dans ces
pages (programme, dates des prochaines sessions, conditions, transparents...).
Finalement j'interviens aussi en tant que consultant pour des missions d'audit, de réalisation de projet ou de tutorat.
12 - L'auteur John COLIBRI est passionné par le développement
Delphi et les applications de Bases de Données. Il a écrit de nombreux livres et articles, et partage son temps entre le développement de projets (nouveaux projets, maintenance, audit, migration BDE, migration Xe_n, refactoring) pour ses clients, le
conseil (composants, architecture, test) et la
formation. Son site contient des articles
avec code source, ainsi que le programme et le calendrier des stages de formation Delphi, base de données, programmation objet, Services Web, Tcp/Ip et
UML qu'il anime personellement tous les mois, à Paris, en province ou sur site client. |