samedi 7 décembre 2013

Transfert de colonne

La table "employés" telle que considérée jusqu'à présent (par exemple ici) présente une anomalie, une atteinte à la normalité: la colonne "service" (qui donne sous forme d'un code le service auquel appartient l'employé) est la seule qui contienne des données se rapportant à l'entreprise.

Transfert vers une nouvelle table

On peut envisager de créer une nouvelle table destinée à contenir les données qui sont à la fois relatives à l'employé et à l'entreprise :

CREATE TABLE gestion (
    matricule INTEGER PRIMARY KEY REFERENCES employés,
    service CHAR(2) REFERENCES services,
    projet CHAR(4) REFERENCES projets,
    barème CHAR(4),
    ancienneté SMALLINT
);

Étant donné les relations  d'intégrité référentielle qui figurent dans l'instruction ci-dessus, la table "projets" destinée à contenir en clair le nom des projets doit bien sûr avoir été préalablement créée:

CREATE TABLE projets (
    projet_id CHAR(4) PRIMARY KEY,
    dénomination CHAR VARYING(25)
);

Avant de supprimer la colonne "service" de la table employés, il faut d'abord en transférer les données vers la nouvelle table gestion.
Nous pouvons procéder avec cette instruction SQL:

insert into gestion (matricule, service)
 select matricule, service
 from employés
 order by matricule
 ;

ou dans un terminal psql (nous utilisons PostgreSQL) avec la méta-commande \copy:

\copy employés (matricule, service) to matr-serv
\copy gestion (matricule, service) from matr-serv

Se connectant  en tant que super-utilisateur postgresql, nous pouvons utiliser la commande COPY:


Notons que le fichier de destination sera créé sur le serveur (même si sur la machine client existe un /var/lib/postgresql).
Ce fichier aura comme propriétaire postgres: il faut donc indiquer un chemin conduisant là où postgres a le droit d'écrire. 
Si le volume de données à traiter est très important, cette solution est à privilégier puisqu'elle évite qu'un nombre de données important ne transite par la connexion client/serveur. 

Transfert vers une table existante

Nous avons transféré le contenu de la colonne "service"  depuis la table "employés" vers une table nouvellement créée.
Nous pouvons aussi considérer que la table cible (gestion) existe déjà.
Ajoutons-y la colonne service:

ALTER TABLE gestion
ADD COLUMN service CHAR(2) REFERENCES services
;

Les relations d'intégrité référentielle existantes impliquent que tous les matricules de la table "gestion" correspondent à des matricules de la table "employés", mais l'inverse n'est pas nécessairement vrai.
Il convient tout d'abord d'insérer dans "gestion" les matricules qui n'y existent pas:

insert into gestion (matricule, service)
 select matricule, service
 from employés
 where not exists
 (select *
  from gestion
  where gestion.matricule = employés.matricule)
 order by matricule
 ;

Les rangées nouvellement insérées dans la table gestion possèdent la bonne valeur de "service" (celle de la table employés). Pour toutes les autres rangées, "service" a la valeur NULL.
Il faut donc effectuer une mise à jour, avec par exemple l'instruction SQL:

UPDATE gestion 
SET service = (SELECT service
FROM employés
WHERE employés.matricule = gestion.matricule)
 ;

Mais alors toutes les rangées (dont les nouvelles) seraient concernées par la mise à jour.
Aussi nous préférons agir plus finement avec cette instruction:

UPDATE gestion
SET service = employés.service
FROM employés
WHERE employés.matricule = gestion.matricule
AND   employés.service <> gestion.service

;

(Remarque: l'utilisation de la clause FROM dans une instruction UPDATE n'est pas autorisée dans le langage SQL standard)

Procédons (après avoir mis l'instruction SQL dans le fichier update-gestion.sql):


Une seule rangée mise à jour! Il y a un problème: nous sommes tombés dans le piège du null.
La seule mise à jour effectuée est due certainement à ce que "service" a changé dans la table "employés" pour une des rangées insérées.
Recommençons après avoir modifié l'instruction pour éviter le piège:



Tout est maintenant en ordre: il reste à supprimer la colonne "service" de la table "employés":

ALTER TABLE employés
DROP service
;

Adaptation des formulaires libreoffice

Évidemment dès cet instant le nom du service ne s'affiche plus dans les formulaires que nous avons considérés  ici et :


C'est normal puisque la zone de liste (listbox) qui fournit en clair le nom du service dépend du sous-formulaire "Employés" dont la source de données est la table "employés":


Nous allons régler le problème en cliquant droit sur l'icône du formulaire "Employés", en vue d'y rattacher un nouveau formulaire



que nous appellerons "Gestion"


La source de données en sera la table "gestion" et le lien avec "Employés" sera basé sur "matricule"


Il reste à effectuer un glisser-déposer des icônes qui conviennent vers le formulaire "Gestion":


Et voilà: