Une interface graphique avec LibreOffice Base
On a une belle base SQLite, c'est très bien. Mais l'interface de DB Browser, c'est plutôt fait pour les gens qui bidouillent la structure de la base et qui ont pas peur du SQL. Ce qu'on veut maintenant, c'est quelque chose de plus confortable pour saisir et consulter les données au quotidien, sans avoir à taper des requêtes à la main. :keyboard:
LibreOffice Base, c'est le pendant libre et gratuit de Microsoft Access. Les deux permettent de créer des formulaires de saisie, des requêtes et des états (des rapports imprimables) par-dessus une base de données. Access est plus répandu dans les environnements Windows institutionnels, LibreOffice Base tourne sur Windows, macOS et Linux. Dans ce support, on va se concentrer sur LibreOffice Base, mais les concepts sont très proches d'Access ; si vous êtes obligé.e d'utiliser Access pour des raisons institutionnelles (la vie est ainsi
), les captures d'écran seront différentes (si je les fais à chaque fois pour les deux, mais je risque d'avoir la flemme, en plus il faudrait que je trouve un ordinateur Windows uniquement pour ça
), mais la logique reste la même.
LibreOffice Base a sa propre base de données interne (HSQLDB), mais on ne va pas l'utiliser. On va le brancher directement sur notre SQLite/GeoPackage existant, pour ne pas avoir à dupliquer les données.
Connecter LibreOffice Base à notre base SQLite
La connexion passe par un pilote ODBC (Open Database Connectivity). C'est un standard universel qui permet à des applications de dialoguer avec des bases de données. Contrairement à d'autres approches, ODBC ne nécessite pas Java, juste un petit pilote à installer sur votre système.
Sur Linux (Ubuntu/Debian)
Installer le pilote :
sudo apt install libsqliteodbc unixodbc
Vérifier les chemins dans /etc/odbcinst.ini :
Après installation, ouvrez /etc/odbcinst.ini et vérifiez que les lignes Driver= et Setup= de la section [SQLite3] contiennent des chemins absolus. Si vous voyez Driver=libsqlite3odbc.so (chemin relatif), corrigez avec cette commande :
sudo sed -i \
-e 's|Driver=libsqlite3odbc.so|Driver=/usr/lib/x86_64-linux-gnu/odbc/libsqlite3odbc.so|' \
-e 's|Setup=libsqlite3odbc.so|Setup=/usr/lib/x86_64-linux-gnu/odbc/libsqlite3odbc.so|' \
/etc/odbcinst.ini
Cette étape est indispensable : sans chemins absolus, LibreOffice plante à l'ouverture de la base sans message d'erreur clair.
(et une mise à jour de LibreOffice peut remettre le problème en place, donc si ça marchait et que ça remarche plus, pensez à revérifier ce fichier)
Déclarer votre base de données :
Créez ou modifiez le fichier ~/.odbc.ini (c'est un fichier texte dans votre dossier personnel) :
[MaBase]
Description=Ma base SQLite
Driver=SQLite3
Database=/chemin/absolu/vers/votre/base.db
Remplacez MaBase par un nom court sans espaces : c'est l'identifiant que vous utiliserez dans LibreOffice. Adaptez le chemin vers votre fichier .db ou .gpkg.
Pour vérifier que tout est prêt avant même d'ouvrir LibreOffice, testez la connexion en ligne de commande :
isql -v MaBase. Si vous voyezConnected!, c'est bon.
Sur Windows
Téléchargez et installez SQLiteODBC depuis http://www.ch-werner.de/sqliteodbc/. Prenez la version correspondant à votre Windows (32 ou 64 bits).
Ensuite, ouvrez les Sources de données ODBC (recherchez "ODBC" dans le menu Démarrer) :
- Onglet Sources de données utilisateur → Ajouter...
- Sélectionnez SQLite3 ODBC Driver
- Donnez un nom à la source (ex :
MaBase) et pointez vers votre fichier.db
Créer la connexion dans LibreOffice Base
Ouvrez LibreOffice Base (Fichier → Nouveau → Base de données) :
- Sélectionnez "Se connecter à une base de données existante"
- Choisissez "ODBC" dans la liste déroulante
- Cliquez sur Suivant
Dans l'écran suivant, cliquez sur "Parcourir" et sélectionnez votre source de données (MaBase).
Cliquez sur "Tester la connexion" : si ça répond "La connexion a été établie avec succès", c'est parti ! 
À l'écran suivant, laissez cochée l'option d'enregistrer les données de connexion dans le fichier .odb (le format natif de LibreOffice Base). Donnez un nom au fichier et enregistrez-le à côté de votre base SQLite. Ce fichier .odb n'est que la configuration de la connexion, vos données restent dans le fichier .db.
SQLite ne gère qu'une écriture à la fois. Si vous ouvrez la base dans DB Browser et LibreOffice Base en même temps et que vous modifiez des données dans les deux, vous risquez des conflits. Travaillez dans un seul outil à la fois.
:baguette_bread: Cette opération n'est à faire qu'une seule fois. Ensuite, LibreOffice enregistre un fichier
.odbqui conserve les informations de connexion en plus des formulaires, requêtes, rapports, etc.
Explorer la base depuis LibreOffice Base
Une fois connecté.e, vous voyez l'interface principale avec quatre sections à gauche : Tables, Requêtes, Formulaires, États.
- Les Tables, j'espère ne pas avoir à vous ré-expliquer ce que c'est.
- Les Requêtes (Queries, parce que j'ai laissé mon interface en anglais, c'est la vie :bald_man:), ce sont des questions que vous pourrez enregistrer, ça peut être croiser des données, mais aussi les filtrer (genre "donne moi les structures des zones de priorité basse") ou même des comptages et statistiques de base. Vous pouvez utiliser ces requêtes pour créer des rapports et pour les statistiques plus complexes, je vous conseille d'exporter des requêtes propres qui seront hyper faciles à intégrer dans R
- Formulaires, c'est assez simple, il s'agit de faire de beaux formulaires de saisie ou de consultation. Vous pouvez réaliser autant de formulaires que vous le désirez sur une même table ou une même requête, dans plusieurs langues aussi. Ce sera le moment de penser à l'ergonomie de votre base.
- États, ce sont les rapports dont j'ai déjà parlé. En gros
, c'est un outil qui permet d'exporter vos données avec une mise en page correcte, comme produire un catalogue ou synthétiser un ensemble d'enregistrements.
Les tables
Cliquez sur "Tables". Vous devriez voir apparaître toutes vos tables SQLite : T_zones, T_mobilier, L_natures_mob, etc. Double-cliquez sur une table pour voir son contenu dans un tableur. Vous pouvez saisir directement dans cette vue, ça peut être pratique dans certains cas, mais c'est rudimentaire. C'est pour ça qu'on va créer des formulaires. 
Créer un formulaire de saisie
Le formulaire de base avec l'assistant
Pour les formulaires on fait simple : on utilise l'assistant. :superhero:
Formulaires → Créer un formulaire en utilisant l'assistant... (ou quelque chose comme ça avec une baguette magique, j'ai directement traduit depuis l'interface en anglais).
L'assistant vous guide pas à pas :
- Sélection des champs : choisissez la table
T_mobilieret déplacez les champs que vous voulez dans le formulaire (pas besoin de mettreid, il est géré automatiquement ougeomqui ici, ne ferait qu'afficher des signes incompréhensibles), en plus, si on se souvient des triggers modifiés, on s'est promis de ne pas toucher au champgeomdans Base. Ajoutezidentifiant,zone,nature,contexte_sol,date_creation,date_modification,auteurice,photographie,commentaires. Ou pour faire plus rapide, cliquez sur le double chevron et retirez les champsidetgeomqui ne nous intéressent pas ici.
Sous-formulaire : on n'en a pas besoin ici, ignorez cette étape. Par exemple, si vous voulez afficher directement les structures des zones dans le formulaire des zones, vous pouvez faire ça ici directement. Pour une liste simple, c'est très pratique, mais si vous voulez un truc un peu poussé, on peut intégrer un sous-formulaire plus tard, à partir d'un formulaire déjà existant (les
3.et4.n'apparaissent que pour les sous-formulaires).Organiser les contrôles : choisissez la disposition qui vous convient. "En colonnes - Étiquettes à gauche" ou "Étiquettes au-dessus", c'est classique, lisible et facile à modifier. Je préfère "Étiquettes au-dessus", mais c'est vraiment du goût perso. :black_cat: D'ailleurs, si vous hésitez, ça affiche le formulaire de façon dynamique quand vous sélectionnez un affichage différent.
Définir la saisie des données : Comme vous allez utiliser cette base vous-même, laissez l'option par défaut : "Le formulaire doit afficher toutes les données" (Là, je suis allé voir la documentation en français donc il ne devrait pas y avoir de problème de traduction).
Style : choisissez ce qui vous plaît, mais évitez les trucs trop chargés si vous voulez rester concentré.e sur les données. De toute façon, tous les trucs de base sont moches !

Nom : appelez-le quelque chose de clair, genre
formulaire_mobilier.
Maintenant, vous pouvez, soit directement travailler avec le formulaire (mais les champs zone, nature, contexte_sol et auteurice affichent des nombres (les clés étrangères) au lieu des libellés) soit modifier le formulaire pour faire de beaux menus déroulants, mettre des étiquettes un peu plus lisible, tout ça, tout ça
(donc moi je fais directement "modifier le formulaire").
Modifier le formulaire
Il faut distinguer les étiquettes (le texte qui indique le nom d'un champ) et le champ lui-même. Si vous cliquez sur un champ, tout est sélectionné à la fois, mais c'est parce que libreOffice fait des groupes par défaut. Pour séparer les deux : clic droit → dégrouper.
Maintenant, si vous cliquez sur une étiquette, vous verrez que ce n'est que de l'affichage. Ça signifie que vous pouvez librement la modifier (mettre des espace, des accents, enfin laissez s'exprimer votre créativité la plus folle !
). Pour le modifier, il suffit de double-cliquer dessus (ou clic droit → Propriétés du contrôle).
Ce qui est bien, c'est que comme ça, on peut aussi modifier les champs. Genre pour identifiant, le wizard il m'a fait une case gigantesque alors que je vais juste mettre quelques lettres à la
(vous l'avez ? Ça se dit comment "vache" en anglais ?). Comme étiquette et champ sont séparés, je clique sur la case et je modifie sa taille. Maintenant, si vous double-cliquez dessus (ou, comme pour les étiquettes, clic droit → Propriétés du contrôle), vous avez le même onglet général que pour l'étiquette, avec un champ pour indiquer l'étiquette liée à champ, mais surtout, vous avez un onglet Données dans lequel vous pouvez sélectionner un champ. C'est en faisant comme ça que vous associez un champ et un contrôle si vous modifiez la structure de votre table plus tard (ça vous évite de tout refaire, c'est tout de même plus pratique).

Transformer un champ en liste déroulante
En dehors des modifications basiques, on veut pouvoir modifier complètement un contrôle. Sur la barre latérale, vous avez plusieurs widgets qui correspondent à des affichages différents. Ça peut être un calendrier, une case à cocher ou même un menu déroulant
!
Dans notre cas, pour la nature du mobilier, on a fait le lien entre la liste des natures de mobilier et la table du mobilier avec une clé primaire. C'est la clé primaire correspondant à un terme qui est enregistré (par exemple 1 pour Céramique). Mais comme nous on veut afficher Céramique et pas 1 (il y a tout de même plus fun qu'apprendre des clés primaires par coeur), on va utiliser un menu déroulant dont le comportement est d'afficher les termes d'un champ choisi pour enregistrer la clé primaire correspondante.
- Clic droit sur
Nature→Dégrouper(l'étiquette et le champ sont groupés par défaut, donc il faut les dégrouper d'abord) - Ensuite supprimez et choisissez dans les contrôles supplémentaires "Zone de liste" et dessinez votre petit rectangle (ou faites directement un clic droit → Remplacer par → Zone de liste)
- L'assistant devrait s'ouvrir et vous demande de choisir la table d'où proviennent les informations (pour nous
L_natures_mob) - Sélection du champ d'affichage → nature_mob
- C'est là qu'on reproduit notre lien, donc c'est hyper important de pas se tromper
Champ de la table des valeur, c'est la table à laquelle est lié le formulaire, là où les valeurs seront enregistrées, le côté n, donc c'est le champnature.Champ de la table de liste, c'est la valeur qui sera enregistrée, le côté 1 du lien, donc on choisit le champid, la clé primaire qui correspond aux valeurs qui seront affichées.
C'est terminé et si tout se passe bien, vous avez un joli menu déroulant qui affiche les bons termes. Vous pouvez basculer de la vue Édition du formulaire à celle normale de remplissage des données avec l'icone qui montre une règle, une équerre et un crayon, ça vous permet de tester vos boutons. Pour que le menu soit raccord avec le reste des contrôles du formulaire (genre les couleurs, la formes, etc.), je vous laisse explorer les Propriétés du contrôle. 
Si jamais vous désirez faire tout ça sans passer par l'assistant (je sais pas moi, je ne vous connais pas et tous les goûts sont dans la nature), ça se passe manuellement comme ça : Dans les propriétés du contrôle → onglet Données :
- Champ de données :
nature(le champ de votre tableT_mobilier) - Type de contenu de liste : "Sql"
- Contenu de liste :
SELECT nom, id FROM T_zones ORDER BY nomça affiche le nom mais enregistre l'id. :exploding_head: Ici, c'est un mélange de SQL et de fonctionnement de libreOffice (logique puisque le SQL ne possède pas une interface graphique native (ben sinon on ne s'embêterait pas à faire tout ça, duh !
)).
L'ordre
SELECT libellé, idest important : le premier champ est affiché, le second est enregistré. C'est l'inverse de ce qu'on ferait intuitivement, mais c'est la convention LibreOffice Base.
Afficher des images
Une question qu'on me pose souvent c'est comment on affiche une image ????. Alors déjà, ça, va pas te crever de dire s'il-te-plaît, on n'a pas élevé les cochons ensemble
! Et ensuite, j'ai jamais pigé pourquoi c'est souvent LE truc le plus important pour les gens et surtout les directeur.ice.s. Enfin c'est comme ça et j'ai une solution donc arrêtons de râler.
En plus, maintenant c'est devenu giga simple.
⇒ clic sur la zone photographie → Dégrouper → supprimer la case du champ photographie → contrôle image dans la barre latérale → on dessine sa petite zone d'image → Propriétés du contrôle → onglet Données → Champ de données → photographie.
Et voilà le travail !
Maintenant, en mode normal, si vous double-cliquez sur la case vide, vous avez un navigateur qui vous permet de sélectionner une image et ça enregistrera le chemin d'accès à l'image tout en affichant une miniature de cette image
!
Car oui, la base de données ne stocke pas elle-même l'image. Ça la rendrait beaucoup trop volumineuse et ingérable rapidement. Le mieux, c'est de les afficher mais de les conserver en externe. Donc si vous donnez votre base et qu'elle contient des images, pensez à donner aussi le dossier des images. Il faut aussi veiller à ce que le chemin d'accès relatif (celui qui part de l'emplacement de votre base vers le dossier des images), soit stable. Le mieux, c'est de créer un dossier images directement là où vous avez mis votre base de données. Comme ça, vous n'avez qu'à transférer la base et le dossier en les gardant simplement ensemble.
Créer une requête graphique
Pour les requêtes, vous pouvez utiliser l'assistant mais perso, j'ai jamais rien pigé à la logique du truc, donc vous pouvez tester le bousin mais moi je ne vais parler que de l'outil de requêtes en mode ébauche... qui propose une interface graphique pour construire des requêtes par glisser-déplacer, sans écrire de SQL à la main.
On va tester un peu les requêtes. Comme c'est pour interroger les données, logiquement, il faut que des données existent ! :exploding_head: Donc remplissez votre base avec quelques données bidon, je me suis limité à
zones,structureetmobilier, ça devrait suffire.
On commence par une requête simple, la liste des structures :
- Ajoutez la table
T_structures - En bas, sélectionnez la table
T_structurespuis le champidentifiant. - Appuyez sur
F5(ou cliquez sur le bon icône mais je ne me souviens jamais exactement duquel (ça devrait être un logo de base de données (les disques empilés) avec un check mark dessus)) et observez le résultat.
Maintenant, si je veux aussi afficher la zone, je peux ajouter le champ zone dans la colonne suivante. Je vous laisse tester.
Si vous avez tout suivi, vous ne devriez pas être surpris.e du résultat. Et si vous êtes surpris.e, bah recommencez parce que j'ai la flemme de tout réexpliquer alors que vous n'avez qu'à mieux suivre, je sais qu'il fait chaud mais tout de même ! 
On va donc légèrement modifier notre requête pour afficher le nom des zones et pas seulement leurs clés primaires :
- Avec l'icone ajouter une table (le tableur avec le gros + vert dessus), la table
T_zones - Comme libreOffice ne lit pas les liens en fait dans SQLite, il faut le recréer. Faites un cliquer-glisser du champ
T.zone.idversT.structures.zone - Dans la deuxième colonne en bas, → Table =
T_zones, Champ =nom
Lancez la requête (F5) et maintenant, c'est bien le nom des zones qui s'affiche à coté des structures.
On va en profiter pour aller voir du côté du SQL. Si vous cliquez sur l'icone de design (celle avec l'équerre à côté d'exécuter la requête), ça fait disparaître l'outil graphique et c'est le code SQL qui apparaît ! 
SELECT "T_structures"."identifiant", "T_zones"."nom" FROM "T_structures", "T_zones" WHERE "T_structures"."zone" = "T_zones"."id"
On en profite pour voir un petit code simple.
SELECTpour choisir les champs qui seront affichés. Ceux-ci sont indiqués avecnom_de_la_table.nom_du_champ. Si le nom du champ est sans équivoque (le même nom n'existe pas dans plusieurs tables sélectionnées), pas besoin de mettre le nom de la table avant de celui du champ.FROM, c'est là qu'on indique les tables utilisées dans la requêteWHERE, la condition. Ici, on veut que les noms de zones affichés correspondent aux clés étrangères de zones des structures affichées.
Si vous tapez cette requête dans DBrowser, vous verrez que vous obtiendrez le même résultat.
Pour améliorer un peu la lisibilité des résultats, on peut mettre des alias (le nom des champs sera renommé sur ces alias en sortie de requête). Par exemple, au lieu d'identifiant et nom, pas très clairs comme ça, on va plutôt indiquer Structure et Zone. Il suffit de les ajouter à la ligne alias, ou en SQL :
SELECT "T_structures"."identifiant" "Structure", "T_zones"."nom" "Zone" FROM "T_structures", "T_zones" WHERE "T_structures"."zone" = "T_zones"."id"
On ajoute un deuxième nom entre guillemets juste après la sélection du champ, ça renommera directement ce champ.
Maintenant une requête plus complexe, je veux les structures situées dans les zones de priorité haute. Je vous laisse deviner avec un indice, dans les colonnes en bas, vous avez une ligne critère dans laquelle vous pouvez insérer une valeur.
Pour la réponse (même si je me doute que vous l'avez déjà
), en interface graphique :
Et en SQL :
SELECT "T_structures"."identifiant", "T_zones"."nom", "L_priorites"."priorite" FROM "T_structures", "T_zones", "L_priorites" WHERE "T_structures"."zone" = "T_zones"."id" AND "L_priorites"."id" = "T_zones"."priorite" AND "L_priorites"."priorite" = 'Haute'
Vous voyez que vous pouvez accumuler et croiser les critères (presque) à volonté, donc ne vous privez pas et allez-y ! 
Pensez à sauvegarder vos différentes requêtes. D'ailleurs, faites-le pour celle des structures et zones, on va l'utiliser dans la section suivante.
J'ai un peu amélioré ma requête des structures et leur zone associée, et ça me donne ça en sortie :
| Structure | Zone | Nature | État |
|---|---|---|---|
| str1 | A | Fossé | Bon |
| str2 | A | Fossé | Mauvais |
| str3 | A | Élévation | Mauvais |
| str4 | C | Élévation | Mauvais |
| str5 | C | Fossé | Mauvais |
| str6 | D | Élévation | Mauvais |
Essayez de reproduire le même résultat pour vous entrainer. Et pour les plus curieux.ses, voici la requête en SQL :
SELECT
T_structures.identifiant,
T_zones.nom AS zone,
L_natures_str.nature_str AS nature,
L_etats.etat AS etat
FROM T_structures
LEFT JOIN T_zones ON T_structures.zone = T_zones.id
LEFT JOIN L_natures_str ON T_structures.nature = L_natures_str.id
LEFT JOIN L_etats ON T_structures.etat = L_etats.id;
On retrouve ici la notion de jointure (JOIN), qui est le cœur des requêtes sur une base relationnelle. L'idée : au lieu de stocker le nom de la zone directement dans T_structures (ce qui créerait de la redondance), on stocke juste son identifiant (zone = 3) et on va chercher le nom correspondant dans T_zones au moment de la requête. (Ça, on le répète depuis longtemps, je pense que c'est compris
) C'est ce que fait le JOIN : il croise deux tables sur une condition (ON T_structures.zone = T_zones.id), et pour chaque structure, il ramène les infos de la zone correspondante.
Aller plus loin : les opérateurs SQL
Il existe plusieurs types de jointures, qui se distinguent par la façon dont elles traitent les lignes sans correspondance. Imaginons deux tables : T_structures (à gauche) et T_zones (à droite). Certaines structures ont une zone renseignée, d'autres non. Certaines zones n'ont aucune structure. Selon le type de JOIN, le résultat sera différent.
INNER JOIN
SELECT T_structures.id, T_zones.nom
FROM T_structures
INNER JOIN T_zones ON T_structures.zone = T_zones.id;
Seules les lignes qui ont une correspondance dans les deux tables apparaissent. Une structure sans zone ? Disparue. Une zone sans structure ? Ignorée. C'est le JOIN le plus restrictif.
LEFT JOIN
SELECT T_structures.id, T_zones.nom
FROM T_structures
LEFT JOIN T_zones ON T_structures.zone = T_zones.id;
Toutes les lignes de la table de gauche (T_structures) apparaissent, qu'elles aient une correspondance ou non dans la table de droite. Les structures sans zone apparaissent avec NULL à la place du nom de zone. C'est le plus utile au quotidien, surtout pendant la saisie où les données sont souvent incomplètes. 
RIGHT JOIN
Même logique mais dans l'autre sens : toutes les lignes de la table de droite apparaissent, même sans correspondance à gauche. En SQLite, le RIGHT JOIN n'est pas supporté directement, mais on peut toujours inverser les tables et utiliser un LEFT JOIN. 
FULL JOIN
Toutes les lignes des deux tables apparaissent, avec NULL quand il n'y a pas de correspondance d'un côté ou de l'autre. Là aussi, pas supporté nativement en SQLite, mais on peut le simuler en combinant un LEFT JOIN et un RIGHT JOIN (ou UNION, on y arrive bientôt).
SELF JOIN
Un SELF JOIN, c'est une table jointe avec elle-même. Ça paraît bizarre, mais c'est utile dès qu'une table contient une relation interne. Exemple classique en archéologie, les tables qui contiennent des périodes chronologiques. On n'a pas toujours accès au même niveau de détail pour des sites et on peut avoir, par exemple, un site simplement daté du Moyen-Âge, un autre du Haut Moyen-Âge et encore un daté du Carolingien. Le Carolingien, c'est une sous-partie du Haut Moyen-Âge, lui même partie du Moyen-Âge. On ne va pas faire plusieurs tables liées par niveau de généralité chronologique, ça deviendrait très compliqué à gérer et, dans cet exemple, notre site ne possède qu'un seul champ "chronologie".
Une table L_periodes avec un champ periode_parente qui pointe vers un autre id de la même table permettrait de modéliser ça proprement :
| id | periode | periode_parente |
|---|---|---|
| 1 | Moyen-Âge | NULL |
| 2 | Haut Moyen-Âge | 1 |
| 3 | Carolingien | 2 |
Et la requête pour afficher chaque période avec le nom de sa période parente :
SELECT
enfant.periode AS sous_periode,
parent.periode AS periode_parente
FROM L_periodes AS enfant
LEFT JOIN L_periodes AS parent ON enfant.periode_parente = parent.id;
Ce qui donne :
| sous_periode | periode_parente |
|---|---|
| Moyen-Âge | NULL |
| Haut Moyen-Âge | Moyen-Âge |
| Carolingien | Haut Moyen-Âge |
La clé, c'est l'utilisation d'alias de table (AS enfant, AS parent dans le FROM et le JOIN) pour distinguer les deux "instances" de la même table. Sans eux, SQL ne saurait pas de quelle copie on parle. Les AS sous_periode et AS periode_parente dans le SELECT sont des alias de colonne, juste pour rendre la sortie plus lisible.
Dans notre base telle qu'elle est, le SELF JOIN n'a pas d'utilité directe, aucune table n'a de relation avec elle-même. Mais si on ajoute cette fameuse table de chronologies, il faudra bien appliquer le concept. :brain:
UNION
Le JOIN croise des tables en colonnes (on ajoute des infos sur chaque ligne). Le UNION fusionne des tables en lignes (on empile des résultats). Exemple : on veut une liste de toutes les entités géolocalisées, structures et anomalies confondues :
SELECT id, 'structure' AS type FROM T_structures
UNION
SELECT id, 'anomalie' AS type FROM T_anomalies;
Les deux SELECT doivent retourner le même nombre de colonnes, dans le même ordre. Le UNION supprime automatiquement les doublons ; si vous voulez les garder, utilisez UNION ALL.
Créer un état (rapport imprimable)
On va créer un état ou rapport (en fonction de libreOffice ou Access, etc.) qui affiche les structures en les regroupant par zone. En clair, une section = une zone et chaque section affiche la liste des structures associées.
Les états permettent de générer des documents imprimables depuis vos données, utile pour un rapport de terrain, une liste d'inventaire, etc. L'assistant est similaire à celui des formulaires. Choisissez votre requête ou table source, sélectionnez les champs, choisissez un regroupement si nécessaire (par zone, par nature...) et laissez l'assistant générer la mise en page.
Là, c'est au choix, assistant ou vue. Ici on va utiliser l'assistant, histoire de faire ça rapidement.
- Sélection des champs : Sur la première fenêtre, je peux choisir une table ou une requête et donc là, avec notre exemple, on choisit ?
⇒ La requête ! Si vous pensiez que c'était une table, bah vous avez rien suivi du tout et je ne vous aime plus !
Sinon, de façon générale, ça vous arrivera probablement souvent de choisir une table. C'est juste que quand on croise des données, autant préparer tranquillement la requête avant.
Pour la suite, on garde toutes les colonnes, le tri a été fait au moment de la requête.
- Étiquettes : Comme ma requête est faite avec des alias, pas besoin de renommer les étiquettes.
- Regroupement : Là on peut choisir
Zone. - Tri : C'est juste pour classer vos éléments, j'ai ajouté
Structure, comme ça, quand il y en a plusieurs par zone, ça les mettra dans l'ordre alphabétique au lieu de leur ordre d'enregistrement (même si, avec mes données, ça change que dalle
). - Affichage : Vous testez ce qui vous plaît le plus.
- Créer le rapport : Nommez-le, ça fait toujours mieux. Statique, le rapport est généré une seule fois et affichera les données telle qu'elles étaient au moment de sa génération. Dynamique, à chaque ouverture, le rapport est à nouveau généré et reflette l'état actuel des données. Ensuite vous pouvez le modifier (ça fonctionne vraiment comme Writer ou Word) ou directement l'ouvrir, là je vais l'ouvrir parce que flemme de l'espace, on est vendredi soir et il faut 34 degrés...

Alors le premier résultat est pas hyper beau, mais je pense que vous avez saisi l'intérêt de l'outil. Vous verrez qu'en modifiant la mise en page on peut vraiment faire BEAUCOUP de choses ! 
Et Access dans tout ça ?
Si vous travaillez sous Windows avec Microsoft Access, la technologie est exactement la même : ODBC avec SQLiteODBC (voir la section Windows ci-dessus pour l'installation et la configuration de la source de données).
Dans Access, la connexion se fait via : Données externes → Base de données ODBC → liez les tables. Les formulaires, requêtes et états se créent ensuite de la même façon. L'interface est différente, les boutons ne sont pas aux mêmes endroits, mais la logique est identique. 
Access ne lit pas les géométries spatiales du GeoPackage. Les colonnes
geomapparaîtront comme du binaire illisible. Pour les données purement attributaires, ça fonctionne très bien. Pour le spatial, restez sur QGIS ou SQLite/SpatiaLite.
Ressources
- Documentation LibreOffice Base : https://documentation.libreoffice.org/fr/
- SQLiteODBC (Windows) : http://www.ch-werner.de/sqliteodbc/
Cette étape est indispensable : sans chemins absolus, LibreOffice plante à l'ouverture de la base sans message d'erreur clair.
(et une mise à jour de LibreOffice peut remettre le problème en place, donc si ça marchait et que ça remarche plus, pensez à revérifier ce fichier)