samedi 6 juillet 2013

Formulaires: rafraîchir sans bouton

Considérons dans libreoffice base un formulaire donnant accès aux données de la table "employés" d'une base de données PostgreSQL à laquelle libreoffice est connecté.
Nous avons détaillé dans ce billet comment constituer un tel formulaire.
Choisissons d'éditer ce formulaire et ouvrons le navigateur de formulaire.
Nous obtenons ceci:


La table "employés" est la source de données du sous-formulaire "Employés".
Cliquer droit dans le navigateur de formulaires sur l'icône "Sélection" :


nous permet d'afficher le panneau des propriétés du formulaire principal "Sélection". Dans notre cas, la source de données de ce formulaire principal "Sélection" est une instruction SQL


Cette instruction fait référence à une table "sélection" dont voici la description:

   Column    |         Type          | Modifiers 
-------------+-----------------------+-----------
 utilisateur | character varying(20) | not null
 matricule   | integer               |

Le sous-formulaire "Employés" est lié à "Sélection" via le champ "matricule" (clef primaire de la table "employés") comme le montre le panneau des propriétés du sous-formulaire "Employés" (accessible bien sûr en cliquant droit sur l'icône "Employés")


Le formulaire contient un bouton 'OK' qui appartient au sous-formulaire "Employés". Quand on clique dessus après avoir encodé un matricule, le focus passe de "Sélection" à "Employés" ce qui provoque l'écriture dans la table "sélection" du matricule encodé. Ensuite l'action attribuée au bouton (à savoir "Rafraîchir le formulaire") est exécutée.
Tout ceci a été expliqué très en détail précédemment et ça fonctionne très bien.
Maintenant nous aimerions nous passer de ce bouton.
"Rafraîchir le formulaire" consiste en fait à recharger (relire) les tables. Afin de pouvoir procéder sans bouton, nous allons assigner la macro

'*************************************
Sub ReloadTableOne
'*************************************
    Dim Frm as object
    Frm=Thiscomponent.DrawPage.Forms.getByIndex(0)
    Frm.reload()
End Sub

à l’événement "Après l'action d’enregistrement"  figurant dans l'onglet "Événement" des propriétés du formulaire "Sélection"
Auparavant nous devons rendre cette macro accessible. Passons par Outils->Macros-> Gérer les macros


et choisissons d'éditer Mes macros->Standard->Modules 1:


Il nous suffit ensuite d'effectuer un copier-coller du texte de la macro:



De cette manière la macro est disponible pour l'utilisateur courant, quelle que soit le fichier .odb ouvert (elle se trouve dans ~/.config/libreoffice/4/user/basic/Standard/Module1.xba).

On peut aussi l'incorporer au document (bd01.odb dans notre exemple): dans ce cas le code ne sera disponible que pour ce document.
Si bd01.odb->Standard->Module1 n'existe pas encore, il faut cliquer sur nouveau pour le créer:



Si le module existe déjà, cliquer sur "Nouveau" nous permet d'ajouter la macro à ce module.
Pour que la macro puisse être utilisée, il faut via Outils->Options->Sécurité->Sécurité des macros


ajouter l'emplacement du fichier aux sources de confiances.

Si tout est en ordre au niveau des macros, un appui sur ENTER après encodage d'un matricule provoquera un rafraîchissement à condition que le focus soit propulsé hors du formulaire principal "Sélection". Pour cela, il faut veiller à faire passer la séquence d'activation du contrôle "fmtmatricule" de 0 à 1.

Qu'en est-il maintenant si à la place du champ formaté permettant de saisir le matricule, le formulaire contient une boite combinée (combobox):


Dans cette boite combinée se trouve une liste ordonnées de matricules issue de la table "employés". La zone de saisie de la boite combinée est liée à la colonne matricule de la table "sélection". Bien sûr si après avoir cliqué sur un élément de la liste, nous appuyons sur ENTER, tout se passe comme dans le cas précédent. L'idéal serait évidemment que l'on ne doive pas appuyer sur ENTER. Il faudrait que le clic propulse le focus quelque part dans le sous-formulaire "employés".
On peut y arriver en assignant une macro (que nous appelons "SetFocusToEmployés") à l'événement "Statut de l'élément modifié" du panneau des propriétés de la boite combinée (onglet "Événement"):


Ce panneau des propriétés est comme d'habitude accessible en cliquant droit sur ce qui convient (ici l'icône combobox1) dans le navigateur de formulaire:


Voici le texte de cette macro:

'*************************************
Sub SetFocusToEmployes
'*************************************
    Dim Champ AS object
    Dim Controleur AS object
    Champ=Thiscomponent.DrawPage.Forms.GetByName("Sélection").getByName("Employés").getByName("txtnom")
    Controleur = Thiscomponent.getCurrentController()
    Controleur.getControl(Champ).setFocus()
End Sub

qui doit être rendue accessible tout comme la macro précédente

Le bouton "OK" est devenu inutile et le formulaire s'emploie maintenant de manière beaucoup plus confortable puisqu'un seul clic sur un matricule permet d'afficher les données concernées.


lundi 3 juin 2013

Le piège du null

Plantons le décor:
Une table "dépenses" créée avec l'instruction:
CREATE TABLE dépenses
(référence CHAR(9) PRIMARY KEY,
 date_exec DATE,
 montant NUMERIC(13,2),
 détails VARCHAR(50),
 code_u CHAR(2))
;
et dans laquelle quelques données ont été placées.

un code_u dont la signification est:

 code_u | signification     
--------+----------------
 01     | Médecin
 02     | Grande surface
 03     | Carburant
 04     | Restaurant

Voilà, le décor est planté.

Examinons le contenu de la table "dépenses", par exemple depuis un terminal psql si nous travaillons avec PostgreSQL:


Nous désirons mettre à jour code_u avec cette instruction:
UPDATE dépenses
SET code_u = '02'
WHERE (détails = 'Courrefar'
OR détails = 'Caro'
OR détails = 'Aupré'
)
AND code_u <> '02'
;
La ligne AND est là pour éviter les mises à jour inutiles (mise à jour des rangées avec code_u valant déjà '02').
En principe 3 rangées devraient être impactées: celles dont la référence se termine par 17, 21 et 22.
Vérifions s'il en sera bien ainsi:


Il manque la rangée 2013-0022. Pourtant rien ne semble la distinguer (par rapport aux critères de sélection) de la rangée 2013-0021.
Poussons plus loin  les investigations.
L'utilisation de la méta-commande \pset null nous permet de choisir ce qui sera imprimé dans le cas de la valeur NULL. Par défaut rien n'est imprimé


Nous voyons maintenant apparaître une différence essentielle entre les rangées 2013-0021 et 2013-0022. Pour la rangée 2013-0021, code_u est caractérisé par une chaîne de caractère vide, ce qui permet de dire que code_u n'est pas égal à 01, ni à 02, 03, 04.(ou sinon la chaîne ne serait pas vide).
Par contre dans le cas de la rangée 2013-0022, l'indication NULL signifie que la base de données ne contient aucune information concernant code_u. Pour cette rangée, code_u est inconnu: il peut très bien valoir '02', où pas. Il est donc impossible de mettre cette rangée dans l'ensemble de celles dont le code_u vaut '02' ou dans l'ensemble de celles dont le code_u est différent de '02'. L'union des 2 ensembles nous fournit 7 rangées (au lieu de 9):


Si le critère de sélection est code_u = '02', les rangées avec code_u NULL se sont pas reprises. Mais elles ne le sont pas non plus si le critère de sélection est code_u différent de '02'.
Et voilà le piège, le piège du NULL dans lequel il ne faut pas tomber! Y tomber, c'est être nul!
L'instruction à utiliser pour la mise à jour devrait être:
UPDATE dépenses
SET code_u = '02'
WHERE (détails = 'Courrefar'
OR détails = 'Caro'
OR détails = 'Aupré'
)
AND (code_u <> '02'
OR code_u IS NULL
)
;

Dans le cas d'une base de données avancée comme PostgreSQL, qui gère l'intégrité référentielle, nous aurions pu après avoir créé une table destinée à contenir les significations de code_u:
CREATE TABLE utilisations
(code_u CHAR(2) PRIMARY KEY,
 signification VARCHAR(20))
;
créer la table "dépenses" comme suit:
CREATE TABLE dépenses
(référence CHAR(9) PRIMARY KEY,
 date_exec DATE,
 montant NUMERIC(13,2),
 détails VARCHAR(50),
 code_u CHAR(2) REFERENCES utilisations)
;
Dans ce cas les seules valeurs autorisées pour code_u auraient été celles qui figurent dans la table "utilisations". Une rangée comme la 2013-0021 n'aurait pas pu exister, mais bien des rangées avec code_u à la valeur NULL.
La notion d'intégrité référentielle a été étudiée dans ce billet.

samedi 18 mai 2013

Passer à l'anglais

Lançant skype depuis un émulateur de terminal, nous obtenons l'avertissement suivant:
(skype:2618): Gtk-WARNING **: Impossible de trouver le moteur de thème dans module_path : « adwaita »
Bien sûr, il s'agit simplement d'un avertissement, mais nous aimerions qu'il n'apparaisse pas.
Une recherche dans google basée sur ce message donne des résultats qui ne permettent pas de trouver une solution au problème.
Peut-être avec comme critère de recherche le même message, mais en anglais, obtiendra-t-on plus de résultats pertinents?
Comment obtenir la version anglaise de ce message?
Voici une solution: dans notre terminal, avant le lancement de skype, exécutons la commande
export LC_ALL=C
Maintenant, nous recevons en retour les messages en anglais:
(skype:2720): Gtk-WARNING **: Unable to locate theme engine in module_path: "adwaita",
Illustration:


Cette manipulation n'est pas seulement utile dans le cas de figure évoqué ci-dessus (recherche basée sur des messages).
Elle permet par exemple d'avoir des pages "man" en anglais: en effet dans le cas où la traduction en français n'est pas très claire, il peut être utile de se rapporter à la langue originale.
Nous pouvons également depuis le terminal lancer la version anglaise des applications.
Par exemple pour libreoffice, on procédera en exécutant la commande soffice.
Afin de revenir à la localisation initiale, il suffit de faire:
unset LC_ALL
Une autre solution pour obtenir les messages de skype en anglais aurait été de taper:
LC_ALL=C skype
ce qui a pour avantage de ne rien changer au niveau du terminal (en fait du shell bash). Il n'est donc plus nécessaire d'utiliser unset.
Enfin, signalons à ceux qui ont atterri ici à partir d'une recherche sur le message d'erreur, la solution au problème était pour notre système fedora 18 64 bits, d'installer adwaita-gtk2-theme.i686.

jeudi 9 mai 2013

Formater une requête avec sed

Dans libreoffice base, nous créons une requête en mode SQL en veillant à la bonne lisibilité du code grâce à un formatage judicieux, comme illustré ci-dessous:


Cependant lorsque plus tard nous sommes amené à éditer cette requête, nous constatons (avec horreur) que notre magnifique formatage a disparu:

Qu'à cela ne tienne, introduisons dans un (émulateur de) terminal (virtuel) la commande

sed -r 's/\,|AND|WHERE|FROM|CASE|WHEN|END/\n&/g'

suivie d'un appui sur la touche ENTER:


Aucun nom de fichier ne lui ayant été fourni, sed est en attente des données qu'il doit traiter et qui vont lui être communiquées via l'entrée standard (stdin).
Dans libreoffice, nous sélectionnons puis coupons l'instruction SQL avant de la coller dans le terminal:


Un appui sur ENTER envoie les données vers sed et nous terminons avec CTRL-D:


Il reste à effectuer un copier-coller de la requête formatée vers la fenêtre d'édition de libreoffice:


Et voilà: nous pouvons maintenant travailler de manière plus agréable.
Si nous sommes intéressé par le procédé, il peut être utile de définir un alias:

alias fq="sed -r 's/AND|WHERE|FROM|CASE|END/\n&/g;s/WHEN/\n  &/g'"

dont la définition sera permanente si elle est ajoutée au fichier ~/.bashrc

(fq est mis pour format query)

Cette fois nous ne considérons pas les virgules car si les traiter était, dans notre exemple,  justifié (pour une meilleure lisibilité), ce ne serait plus le cas en de présence de fonctions dont les arguments sont séparés par des virgules

mardi 16 avril 2013

Bilans mensuels

Dans notre base de données de test (bdtest), nous créons la table opérations en exécutant l'instruction SQL:

CREATE TABLE opérations
(reference CHAR(9) PRIMARY KEY,
date_exec          DATE,
montant            NUMERIC(13,2),
contrepartie       CHAR(5))
;

S'agissant d'une base de données PostgreSQL, il nous suffit par exemple de coller cette instruction dans un terminal psql connecté à bdtest. Si l'instruction est contenue dans un fichier, nous pouvons également procéder avec la méta-commande \i:

bdtest=> \i fichier

Il nous reste à peupler cette table. Nous avons à cette fin constitué un fichier nommé opérations contenant quelques données:

2012-0458 2012-12-30 487.33  RN2
2013-0001 2013-01-03 1874.56 RN1
2013-0002 2013-01-03 -42.57  ceci
2013-0003 2013-01-14 -119.23 cela
2013-0006 2013-01-31 -956.00 Loyer
2013-0007 2013-01-31 1874.56 RN1
2013-0008 2013-02-01 -127.23 GS
2013-0009 2013-02-09 -47.00  Autre
2013-0010 2013-02-11 -845.99 TV
2013-0011 2013-02-14 -25.47  Div
2013-0012 2013-02-15 -49.00  Ceci
2013-0013 2013-02-20 -314.00 VOIT
2013-0014 2013-02-27 -103.49 Tél
2013-0015 2013-02-28 -956.00 Loyer
2013-0016 2013-02-28 1874.56 RN2
2013-0017 2013-03-01 -117.23 GS
2013-0018 2013-03-06 -43.00  Autre
2013-0019 2013-03-06 -76.84  Div
2013-0004 2013-01-14 -314.00 VOIT
2013-0005 2013-01-31 -114.23 Tél

Le caractère de séparation entre les colonnes est le caractère de tabulation et nous utilisons la méta-commande \copy:

bdtest=> \copy opérations from opérations

Si au lieu de travailler dans une base de données PostgreSQL nous préférons utiliser la base de données intégrées à Libreoffice base, nous pouvons tant pour la création de la table que pour son remplissage procéder comme montré dans ce message.

Nous allons créer une vue qui placera les montants positifs (crédits) dans une colonne et les montants négatifs (débits) dans une autre colonne.
Nous aurons aussi besoin de l'année et du mois de chaque opération. Ceux-ci peuvent être extraits de la date d'exécution comme ceci:
extract(year from date_exec) pour l'année
extract(mont from date_exec) pour le mois.

Mais comme montré ici:


le résultat n'est peut-être pas très heureux en ce qui concerne les numéros de mois: 01 au lieu de 1 serait plus esthétique.
Aussi, il est préférable d'utiliser la fonction to_char:
to_char(date_exec, 'YYYY') pour l'année
to_char(date_exec, 'MM') pour le numéro du mois
et nous pouvons même obtenir le nom du mois avec
to_char(date_exec, 'TMMONTH'):


Cela étant, créons la vue opérationsv à l'aide de l'instruction SQL suivante, en procédant comme pour la création de la table.

CREATE VIEW opérationsv
AS
SELECT
     to_char(date_exec, 'YYYY') AS an,
     to_char(date_exec, 'MM') AS mois_n,
     to_char(date_exec, 'TMMONTH') AS mois,
     reference,
     date_exec,
     CASE
       WHEN montant > 0 THEN montant
       ELSE 0.00
     END AS crédit,
     CASE
       WHEN montant < 0 THEN -montant
       ELSE 0.00
     END AS débit,
     montant AS solde,
     contrepartie
FROM opérations

Si nous travaillons dans la base de données intégrée à libreoffice, l'expression to_char(date_exec, 'TMMONTH') n'est pas reconnue. Il convient alors de la remplacer par:

CASE
  WHEN extract(month from date_exec) = 1 THEN 'Janvier'
  WHEN extract(month from date_exec) = 2 THEN 'Février'
  WHEN extract(month from date_exec) = 3 THEN 'Mars'
  WHEN extract(month from date_exec) = 4 THEN 'Avril'
  .....
END AS mois,

Et nous voilà paré pour sortir des bilans, par exemple avec une simple instruction SQL comme celle-ci:

select an, mois, count(*) as opérations,
sum(crédit) as crédit,
sum(débit) as débit,
sum(solde) as solde
from opérationsv
group by an, mois_n, mois
order by an, mois_n
;

Voici le résultat dans un terminal psql:


Il est possible d'améliorer l'output obtenu.
Plaçons dans un fichier bilan-mensuel.sql les commandes

\set QUIET
\pset numericlocale on
\pset footer off
\pset linestyle u
\pset title 'Bilan 2013'
\pset border 2
\o | awk -f bilan-mensuel.awk | tee rapport.txt
select mois_n, mois, count(*) as opérations,
sum(crédit) as crédit,
sum(débit) as débit,
sum(solde) as solde
from opérationsv
where an = '2013'
group by mois_n, mois
UNION
select 'TOTAL', '', count(*) as opérations,
sum(crédit) as crédit,
sum(débit) as débit,
sum(solde) as solde
from opérationsv
where an = '2013'
order by mois_n
;
\o
\pset footer
\pset title
\pset border 1
\unset QUIET


Et voici le résultat:


L'output a été redirigé vers la commande awk pour exécution du script bilan-mensuel.awk

# Impression du titre
NR == 1 {
print "\n" $0 "\n"
}
# Sauvegarde ligne de séparation dans y
NR == 4 {
y=$0
}
# Imression d'une ligne de séparation si
# la ligne en input contient TOTAL en position 3
/^..TOTAL/ {
print y
}
# Impression des lignes en input
# sauf le titre déjà imprimé
NR > 1

dont le rôle essentiel est d'imprimer une ligne de séparation avant la ligne 'TOTAL' et accessoirement d'imprimer une ligne vide avant et après le titre.
De plus, grâce à la commande 'tee', le fichier rapport.txt contient une copie de ce qui a été envoyé à l'écran. Ce fichier rapport.txt peut tout à fait être intégré dans un traitement de texte.
Nous avons déjà traité de la technique utilisée ici pour améliorer la présentation au niveau d'un terminal psql dans ce billet et le suivant.
Pour confectionner un rapport mieux présenté et où figurent en plus les lignes de détails, il convient de travailler dans libreoffice base avec ORB 'Oracle report builder'. Pour disposer de cette extension il suffit le plus souvent d'installer un paquet dont le nom est (ou ressemble à) 'libreoffice-report-builder'.
Si nos données figurent dans bdtest, il est évidemment nécessaire de connecter libreoffice à cette base de données, opération pour laquelle il est grandement conseillé d'installer le paquet 'libreoffice-sdbc-postgresql' (ou un paquet dont le nom ressemble à celui-là).
Dans le rapport que nous envisageons de confectionner, il est relativement inutile de placer dans les lignes 'détails' le contenu du champ 'solde' tel qu'il est défini dans la vue opérationsv, puisque que celui-ci est toujours égal à un des deux montants crédit ou débit. Par contre y mettre le solde résultant de toutes les opérations réalisées depuis le début du mois est nettement plus intéressant.
A cette fin, nous allons dans libreoffice base créer en mode SQL la requête opérationsr:

SELECT "A".*,
( SELECT SUM( "solde" )
  FROM "opérationsv" "B"
  WHERE "B"."reference" <= "A"."reference"
  AND "B"."an" = "A"."an"
  AND "B"."mois_n" = "A"."mois_n" 
) AS "solde_c"
FROM "opérationsv" "A"

Après avoir choisi 'Créer un rapport en mode ébauche', nous sélectionnons cette requête en tant que de source de données que nous filtrons sur une année:



Pour le reste,  nous allons procéder comme expliqué dans ce billet, le rapport réalisé alors nous servant de modèle.
Nous n'allons pas ici entrer dans les détails, mais fixer simplement quelques points de repères.

Dans la  fenêtre 'Tri et groupements' nous plaçons mois-n et reference:

Les données sont groupées suivant mois_n, mais cela ne nous empêche pas de faire figurer le nom du mois en clair dans l'en-tête de groupe:


Les valeurs dans la colonne 'solde' sont tirées de solde_c sauf pour la fonction 'Accumulation' qui porte sur  'solde'.

Et voici le résultat obtenu:


(EDIT: voir variante ici)

Dans le cas d'un rapport plus conséquent, il peut être utile de forcer une nouvelle page pour chaque changement de mois:



Nous pouvons également envisager d'introduire un groupement supplémentaire sur la date:


Établir un rapport tel que celui-ci


tenant compte de ce nouveau groupe ne pose aucun problème particulier.

La requête 'source de données' conduisant à ce résultat est cette fois:


SELECT "A".*,
( SELECT SUM( "solde" )
FROM "opérationsv" "B"
WHERE "B"."reference" <= "A"."reference"
AND   "B"."date_exec" = "A"."date_exec" ) AS "solde_c"
FROM "opérationsv" "A"



Comme auparavant les valeurs de la colonne 'solde' sont tirées de 'solde_c' sauf pour les fonctions 'Accumulation' (Total mois et Total) où intervient 'solde':


x

mercredi 2 janvier 2013

Import dans libreoffice base

Dans ce billet nous allons montrer comment facilement importer les données d'un fichier de type CSV dans une base données HSQL (base de donnée intégrée à libreoffice).
A l'ouverture de libreoffice base, nous choisissons de nous connecter à une base de données existante de type texte.
Tiens, on ne vient pas, là tout de suite, de parler de base de données HSQL?
Oui, mais cela ce sera pour plus tard.
Donc nous choisissons de nous connecter à une base de données existante de type texte:


Nous avons préalablement créé un dossier ~/Documents/bdcsv destiné à contenir les tables de notre base de données (qui sont en réalité des fichiers de type CSV) et nous en indiquons le chemin au moment de paramétrer la connexion vers les fichiers textes. Nous ne manquons pas de paramétrer le point-virgule comme étant le séparateur de champ (s'il en est ainsi pour nos fichiers CSV) et de choisir pour séparateur de décimales celui qui convient:


Nous terminons en enregistrant la base de données sous le nom bdcsv. 
A l'ouverture du fichier bdcsv.odb, nous constatons que la base de données ne contient aucune table.
Normal puisque ~/Documents/bdcsv est vide.
Nous y plaçons le fichier opérations.csv contenant les donnée à importer:

id_op;date_op;montant;contrepartie
2011-0458;2011-12-30;487.33;RN2
2012-0001;2012-01-03;1874.56;RN1
2012-0002;2012-01-03;-42.57;ceci
2012-0003;2012-01-14;-119.23;cela
2012-0004;2012-01-14;-314.00;VOIT
2012-0005;2012-01-31;-114.23;Tél
2012-0006;2012-01-31;-956.00;Loyer
2012-0007;2012-01-31;1874.56;RN1
2012-0008;2012-02-01;-127.23;GS
2012-0009;2012-02-09;-47.00;Autre
2012-0010;2012-02-11;-845.99;TV
2012-0011;2012-02-14;-25.47;Div
2012-0012;2012-02-15;-49.00;Ceci
2012-0013;2012-02-20;-314.00;VOIT
2012-0014;2012-02-27;-103.49;Tél
2012-0015;2012-02-29;-956.00;Loyer
2012-0016;2012-02-29;1874.56;RN2
2012-0017;2012-03-01;-117.23;GS
2012-0018;2012-03-06;-43.00;Autre
2012-0019;2012-03-06;-76.84;Div

Pour voir apparaître l'icône de la table, nous passons par le menu Affichage → Actualiser les tables :


Cliquant droit sur l'icône de la table qui est apparue


nous sélectionnons 'Éditer' dans le menu contextuel qui surgit  afin de vérifier quel type a été attribué aux différents champs de la table:


Nous vérifions que pour le champ 'montant' les décimales ont bien été prises en compte.
Nous constatons que date_op est bien au format 'Date'. Ce ne serait pas le cas si une seule rangée de la table contenait une date invalide : date_op serait alors au format 'Texte' ce qui ne manquerait pas de poser problème pour la suite.
La suite ? Quelle suite ?
Et bien, rappelons le,  le but est d'importer les données du fichier opérations.csv dans une base de données intégrée à libreoffice (HSQL).
bdcsv.odb est en fait une base de de données de transit, un réservoir de données à importer dans la véritable base de données finale. Chaque fois que nous disposerons de données au format CSV à importer, il nous suffira avant de procéder de placer le fichier de données dans ~/Documents/bdcsv.
Attention: si le fichier en question utilise le séparateur de texte, toutes les données concernées seront de type texte. Si ce n'est pas souhaité, il convient alors de supprimer le dit séparateur par exemple en utilisant sed:
sed -r -i.old 's/\"//g' fichier.csv
Lançons maintenant une deuxième instance de libreoffice en choisissant au niveau du menu d’accueil l'option 'Créer une base de données' et nous terminons en enregistrant cette base de données sous le nom 'opérations'. Et voici notre base de données finale sur les rails!
Ensuite, plutôt que d'utiliser le mode 'Ébauche' pour créer la table où placer les données à importer, nous procédons via le menu Outils → SQL:


Dans la fenêtre 'Instruction SQL', nous collons l'instruction:

CREATE TABLE opérations
(reference CHAR(9) PRIMARY KEY,
date_exec DATE,
montant NUMERIC(13,2),
contrepartie CHAR(5))
;

et nous appuyons sur le bouton exécuter:



La table nouvellement créée sera visible après utilisation du menu Affichage -> Actualiser les tables. Nous constatons que la table a été créée sous le nom 'OPÉRATIONS':


Effectuons un 'glisser-déposer' de l'icône bdcsv.odb->opérations dans la zone des Tables de opérations.odb. Dans la fenêtre qui surgit nous remplaçons le nom de la table source (opérations) par celui de la table cible (OPÉRATIONS) et nous cochons 'Ajouter des données' :


L'appui sur le bouton 'Suivant' nous permet de vérifier la correspondance des colonnes.


L'appui sur 'Créer' finalise l'opération d'importation.

Il est plus facile (comme nous l'avons fait) mais pas nécessaire de créer au préalable la table destinée à recevoir les données que nous souhaitons importer.
Si cette table n'existe pas, dans la fenêtre 'Copier la table', nous cochons 'Définition et données' ainsi que 'Créer une clé primaire' à laquelle nous donnons le nom id_op. Mais ceci nous conduit à une impasse:


Aussi nous recommençons après avoir remis le nom par défaut et après avoir décoché 'Créer une clé primaire':


Le bouton 'Suivant' nous donne le choix des colonnes à importer :


Nous pouvons ensuite modifier à notre guise le type des différents champs, par exemple pour id_op passer du type TEXTE[VARCHAR] au type TEXTE (FIXE)[CHAR].
Il ne faut pas oublier de mettre le nombre de décimales à 2 pour le champ 'montant' :


Après avoir appuyé sur 'Créer', nous répondons 'Oui' à la question 'Créer une clef primaire' car sans cela il nous sera impossible par la suite de définir id_op comme clé primaire:


Évidemment ceci ajoute une colonne supplémentaire (ID par défaut) à la table, colonne que nous supprimons illico.
Pour ce faire nous cliquons droit sur l'icône de la table qui vient d'être créée et nous sélectionnons 'Editer' dans le menu contextuel qui surgit. Ensuite dans la fenêtre 'Table design' nous cliquons droit en tête de la ligne définissant ID et nous choisissons 'Supprimer' :


Puis comme il nous faut une clef primaire pour pouvoir éditer les données, nous cliquons droit en tête de la ligne définissant id_op et nous choisissons 'Clef primaire' :


Et voilà!