From 9f8ee4f77ce1d8018a5afa661555a4a553b164a5 Mon Sep 17 00:00:00 2001 From: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> Date: Tue, 31 Dec 2024 11:47:55 +0100 Subject: [PATCH 001/101] Add files via upload Proposition d'article sur une introduction au json dans PostgreSQL Signed-off-by: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> --- content/articles/2024/2024-12-31_sql_json.md | 549 +++++++++++++++++++ 1 file changed, 549 insertions(+) create mode 100644 content/articles/2024/2024-12-31_sql_json.md diff --git a/content/articles/2024/2024-12-31_sql_json.md b/content/articles/2024/2024-12-31_sql_json.md new file mode 100644 index 0000000000..3b945a3353 --- /dev/null +++ b/content/articles/2024/2024-12-31_sql_json.md @@ -0,0 +1,549 @@ +--- +title: "Travailler avec du json et PostgreSQL" +subtitle: Jason et les éléphants +authors: + - Thomas SZCZUREK-GAYANT +categories: + - article +comments: true +date: 2024-12-31 +description: "Stocker des données au format json dans PostgreSQL, les consulter... et tout ça avec les données du recensement de l'INSEE pour l'exemple. " +icon: simple/postgresql +image: +license: default +robots: index, follow +tags: + - PostgreSQL + - json +--- + +## Travailler avec du json dans PostgreSQL + +Dans le cadre d'un projet personnel, j'ai voulu stocker une bonne partie des données du recensement de l'insee dans une base PostgreSQL avec des tables multimillésimes. Problème, même au sein d'un même jeu de données, les champs peuvent changer au cours des années et celà empêche de pouvoir dégager une structure de table fixe, ce qui est assez génant vous en conviendrez. La solution ? Passer par des données semi-structurées, soit stocker ces données en json dans le champ d'une table. Cet article se veut un condensé de cet expérience. + +!!! warning + Ces travaux ont été réalisés avant la sortie de PostgreSQL 17 qui ajoute d'importantes fonctionnalités pour le json comme les [`JSON_TABLE`](https://doc.postgresql.fr/17/functions-json.html#FUNCTIONS-SQLJSON-TABLE), elles ne seront ici pas évoquées. + +Puisque nous allons parler de json et de données semi-structurées, je me sens dans l'obligation de commencer cet article par un avertissement. + +**Le modèle relationnel c'est bon, mangez en, et les contraintes d'intégrités ont été inventées pour de bonnes raisons.** + +Cet article ne se veut surtout pas être une invitation à partir en mode yolo sur la gestion des données "c'est bon ya qu'a tout mettre en json" (comme un vulgaire dev qui mettrait tout dans mongodb diraient les mauvaises langues). + +### Le json pour les débutant.es + +Pour celles et ceux qui ne connaisent pas le `json`, il s'agit d'un format textuel de représentation des données fonctionnant en partie sur un système de `clé : valeur` qu'on peut voir comme une sorte d'évolution du `xml`. + +```json +{"clé_1": "valeur", "clé_2": "valeur", "clé_3": "valeur"} +``` + +(pas besoin de guillemets pour les nombres) + +```json +{"nb_champignons": 42, "nb_tomates": 31, "prenom": "roger"} +``` + +Les valeurs peuvent prendre deux formes. Soit une valeur unique comme dans l'exemple ci-dessus, soit un `array` (une liste), les deux pouvant êtres combinés au sein d'un seul objet json. + +```json +{"prenoms": ["elodie", "roger", "fatima"], "nb_champgnons": 42} +``` + +Ce qu'on appelle un objet, c'est tout ce qu'il se trouve entre les `{}` qui servent à déclarer le dit objet. Pour complexifier tout ça, on peut imbriquer les objets et ainsi vous donner un exemple un peu plus parlant que de parler de tomates et de champignons : + +```json +{ + "squadName": "Super hero squad", + "homeTown": "Metro City", + "formed": 2016, + "secretBase": "Super tower", + "active": true, + "members": [ + { + "name": "Molecule Man", + "age": 29, + "secretIdentity": "Dan Jukes", + "powers": [ + "Radiation resistance", + "Turning tiny", + "Radiation blast" + ] + }, + { + "name": "Madame Uppercut", + "age": 39, + "secretIdentity": "Jane Wilson", + "powers": [ + "Million tonne punch", + "Damage resistance", + "Superhuman reflexes" + ] + }, + { + "name": "Eternal Flame", + "age": 1000000, + "secretIdentity": "Unknown", + "powers": [ + "Immortality", + "Heat Immunity", + "Inferno", + "Teleportation", + "Interdimensional travel" + ] + } + ] +} +``` + +Exemple issu du site de [Mozilla](https://developer.mozilla.org/fr/docs/Learn/JavaScript/Objects/JSON) qui vous permettra d'approfondir. Vous pouvez aussi consulter l'article [wikipedia](https://fr.wikipedia.org/wiki/JavaScript_Object_Notation) ou encore [l'infâme site de la spécification](https://www.json.org/json-fr.html). + +### Les types json de PostgreSQL + +PostgreSQL est capable de stocker les données/objets au format json dans des champs auxquels on attribuera un type dédié. Il y en a deux car sinon ça ne serait pas rigolo. + +- Le type `json` qui est là pour des raisons historiques et laisser fonctionner des bases qui auraient utilisé ce type dans le passé. Il stocke les informations sous forme textuelle ce qui peu optimisé pour un ordinateur. Il existe toutefois un intérêt à l'utiliser : il permet de retrouver l'information sur l'ordre des clés. Si il est important pour vous de savoir que `nom` est la clef 1 et `prenom` la clef 2, sans avoir à repasser par le nom de la clef, alors il vous faudra passer par le type `json`. + +- le type `jsonb`. Le type moderne. Il stocke les informations sous forme binaire et énormément de fonctions sont disponibles en plus de celles du type `json`. + +### Les index + +Il est possible d'indexer un champ de type `json` / `jsonb` sur ses clés **de premier niveau** et ça se fait avec des index de type `GIN`. + +```sql +CREATE INDEX idx_tb_champjson ON tb USING gin (champ_json); +``` + +Pour indexer des valeurs plus "profondes", il faudra passer par des indexs fonctionnels (index sur des fonctions) + +```sql +CREATE INDEX idx_tb_champjson ON tb USING gin (champ_json -> cle_ou_se_situent_les_valeur_a_indexer); +``` + +(on va expliquer ce `->` par la suite) + +Pour les aventuriers et aventurières, il existe une extention de PostgreSQL `btree_gin` permettant de faire des index multi-champs mixant des champs classiques et json. Elle est disponible nativement à l'installation de PostgreSQL et ne vous demandera pas de devenir développeur.se C/C++ pour l'installer (coucou les fdw parquet ! Ca va par chez vous ?). + +### Création des tables + +Je vais éviter de vous spammer du DDL, mais vous pourrez retrouver le schéma complet de la base [ici](https://github.com/thomas-szczurek/base_donnees_insee/tree/main/sql/creation_tables) + +Partant d'un schéma nommé `insee`, on va créer deux tables. La première contiendra la liste des *bases* disponibles (les différentes volets du recensement) et une seconde permettant de stocker les données (pour rester concentré sur le json, on va s'épargner 95% du modèle sous jacent, on ne gérera donc par ici les codes communes etc...). En bonus pour celles et ceux voulant tester ce que Postgres a dans le ventre, je proposerai une variante juste en dessous : + +```sql +BEGIN +-- table listant les differentes bases insee disponibles + +CREATE TABLE insee.bases ( +pk_id int2 PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY +nom text NOT NULL +); + +-- table stockant les données par communes + +CREATE TABLE insee.donnees_communes ( +pk_id int4 PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, +code_commune text NOT NULL CHECK length(code_com) = 5, -- n'oublions pas les Corses qui ont des codes commune en 2A et 2B d'où le type text +fk_base int2 NOT NULL, +annee int2 NOT NULL, +donnees jsonb NOT NULL, +UNIQUE (fk_base, annee, code_commune) +CONSTRAINT fk_donnees_bases FOREIGN KEY fk_base ON insee.bases(pk_id) ON UPDATE CASCADE ON DELETE CASCADE +); + +-- Création des index +-- Un index multichamps est créé automatiquement sur (fk_base, annee, code_commune) grace à la clause UNIQUE +CREATE INDEX idx_donnees_com_donnees ON insee.donnees_communes USING gin (donnees); +END; +``` + +Les plus tatillons des dba d'entre vous auront remarqués ce `CHECK`. "Mais pourquoi qu'il utilise pas varchar(5), il fait vraiment n'importe quoi". Tout simplement car cette forme permet d'utiliser un type de texte aux nombre de caractères **réellement** arbitraire (le type `text`) contrairement à varchar(256), tout en pouvant en contrôler le nombre minimum et maximum avec le prédicat `CHECK` (contrairement à varchar qui ne contrôle que le maximum) comme expliqué sur le [wiki Postgres](https://wiki.postgresql.org/wiki/Don%27t_Do_This#Don.27t_use_varchar.28n.29_by_default). + +Et on insère quleques ligne dans notre table insee.bases + +```sql +INSERT INTO insee.bases (nom) VALUES +('rp_population'), +('rp_menages'), +('rp_logement'), +('rp_diplomes'), +('rp_activite'), +('rp_emploi') +``` + +#### (Bonus) Partition de la table données + +Imaginons que nous travaillons sur l'ensemble des données du recensement (soit 6 fichiers sources), de 2015 à 2021 pour environ 35 000 communes. On va arriver sur du 1,5 millions d'enregistrements. Entendons nous bien, à l'échelle de Postgres ça ne reste pas grand chose. Mais, si comme moi vous voulez voir ce qu'on peut tirer des entrailles de Postres, ça permet de pouvoir commencer à justifier d'utiliser certaines fonctionnalités avancées. Une table partitionnée c'est quoi ? + +- C'est une table découpée en plusieurs morceaux où une table *parent* contrôlera les tables *enfants*. +- Une table partitionée peut se découper selon la valeur d'un champ ou une période temporelle ("fait moi une partition tous les mois") +- On peut requêter la table parent ou les tables enfants. +- Une table doit être partitionnée à sa création, une table déjà existante ne peut pas être convertie. De nouvelles partitions peuvent toutefois êtres créées à volonté. +- Celà peut être interessant pour limiter la taille des scans séquentiels qui se feront sur des tables plus petites que la table *parent*, où pour se faciliter la gestion de tables volumineuses. +- On peut attacher / détacher une partition (qui devient alors une table classique) avec les "mots" `ATTACH PARTITION` / `DETACH PARTITION` y comprit sur une base en production grace à l'option `CONCURENTLY` +- Une clé primaire de table partitionnée **doit** être composite, c'est à dire que son unicité sera vérifiée par la composition de plusieurs champs, et contenir obligatoirement le champ de partitionnement. + +(vous aussi lire le contenu de la [documentation à se sujet](https://doc.postgresql.fr/17/ddl-partitioning.html)) + +A quoi ressemblerai la création de insee.donnees_communes si on la partitionnait selon les différentes sources de données ? + +```sql +BEGIN +CREATE TABLE insee.donnees_communes ( + pk_id int4 GENERATED BY DEFAULT AS IDENTITY, + code_commune text NOT NULL CHECK length(code_com) = 5, + annee int2 NOT NULL, + fk_base int2 NOT NULL, + donnees jsonb NOT NULL, + UNIQUE (annee, code_commune, fk_base), + CONSTRAINT pk_donnees_communes PRIMARY KEY (pk_id, fk_base), + CONSTRAINT fk_donnees_bases FOREIGN KEY (fk_base) REFERENCES insee.bases (pk_id) ON UPDATE CASCADE ON DELETE CASCADE +) PARTITION BY LIST (fk_base); +CREATE TABLE insee.donnees_communes_fk_1 PARTITION OF insee.donnees_communes FOR VALUES IN (1); +CREATE TABLE insee.donnees_communes_fk_2 PARTITION OF insee.donnees_communes FOR VALUES IN (2); +CREATE TABLE insee.donnees_communes_fk_3 PARTITION OF insee.donnees_communes FOR VALUES IN (3); +CREATE TABLE insee.donnees_communes_fk_4 PARTITION OF insee.donnees_communes FOR VALUES IN (4); +CREATE TABLE insee.donnees_communes_fk_5 PARTITION OF insee.donnees_communes FOR VALUES IN (5); +CREATE TABLE insee.donnees_communes_fk_6 PARTITION OF insee.donnees_communes FOR VALUES IN (6); +END; +``` + + +### Insertion de données et récupération + +Pour passer des données textuelles / sql vers des données encodées en json dans le champ dédié qui va bien, PostgreSQL dispose de [*quelques* fonctions](https://docs.postgresql.fr/17/functions-json.html#FUNCTIONS-JSON-PROCESSING). Nous allons utiliser la fonction `jsonb_object()` qui permet de transformer un `array` sql sous forme `clef1, valeur1, clef2, valeur2 ....` en objet `jsonb` qui n'aura qu'un niveau d'imbrication. D'autres fonctions sont disponibles pour des objets plus complexes (comme `jsonb_build_object()`). + +#### Exemple simple + +On va créer une chaine de texte qui contiendra le contenu de notre json dont les valeurs serons séparées par des virgules `clé1,valeur1, clé2, valeur2`. Cette chaine sera passée dans une fonction `string_to_array()` la transformant en `array` avec comme séparateur des `,` pour séparer les éléments de la chaine de texte vers des éléments de liste, caractère passé en second paramètre de la fonction. Cet `array` sera ensuite envoyé dans la fonction `jsonb_object()`. + +```sql +INSERT INTO insee.donnees_communes (code_commune, annee, fk_base, donnees) VALUES +( + '99999', + 2024, + 1, + json_object( + string_to_array('tomates,42,melons,12',',') + ) +) +``` + +Cette requête encodera cet objet json dans le champs `données` : + +```json +{ + "tomate": 42, + "melons": 12 +} +``` + +Maintenant comment récupérer notre nombre de melons pour le code commune 99999 en 2024 ? Celà se fait grace à des opérateurs spéciaux : + +- `champ_jsonb -> 'clé'` récupère la valeur d'une clé en concervant son type json +- `champ_jsonb ->> 'clé'` fait de même en transformant la valeur en type sql "classique" + +```sql +SELECT + (donnees ->> 'melons')::int4 AS nb_melons +FROM insee.donnees_communes +WHERE + donnees ? 'melons' +``` + +Vous pouvez voir que j'utilise l'opérateur `?` (uniquement vable pour les champs `jsonb` et non ceux en simple `json`). En effet, lorsque l'on requête un champ json/jsonb, un retour vous est fait pour l'ensemble les enregistrements de la table, même ceux ne contenant pas la clé. Comprendre que si votre table comprends 100 000 enregistrements, mais que seuls 100 contiennent la clé "melons", ne pas spécifier cette clause WHERE vous renverait 100 000 lignes, dont 99 900 de `NULL`.`?` est un opérateur json permettant de poser la question "la clé est-elle présente au premier niveau du champ json pour cet enregistrement ?", et on ne récupérerai que nos 100 enregistrements contenant la clé "melons". + +Je précise, même si, si vous êtes encore ici je suppose que vous savez déjà cela, mais la forme `(quelque_chose)::int4` est un raccourcis de PostgreSQL pour faire un `cast` soit convertir une valeur dans un autre type. Avec `->>` la valeur nous est renvoyée sour forme de texte et nous la convertissons en entier. + +#### Avec du json imbriqué + +Bon, il est bien gentil, mais là son json reste du json où tout est au premier niveau. Bah oui, pour mon besoin cela m'a suffit. Mais je ne vais pas fuir mes responsabilités et on va voir comment cela se passe avec du json plus complexe. Je repartirai de l'exemple de la partie précedente pour compléxifier après cet apparté. + +Pour injecter du json complexe dans un champs, deux solutions s'offrent à nous : Imbriquer les fonctions dédiées, ou caster une chaine de texte. Imaginons un table "test", dans un schema "test" et dont un champ `jsonb` se nomme "données". + +```sql +INSERT INTO test.test (donnees) VALUES +(jsonb_object( + 'cle1', 'valeur1', + 'cle2', jsonb_array( + 'foo', 'bar', 'baz') + ) + ) +``` + +Cette insertion pourrait tout aussi bien s'écrire avec un cast d'une chaine de texte vers du jsonb, attention, la syntaxe json doit être ici respectée : + +```sql +INSERT INTO test.test (donnees) VALUES +('{"cle1": "valeur1", "cle2": ["foo", "bar", "baz"]}'::jsonb) +``` + +Pour récupérer une valeur, on utilise la fonction `jsonb_path_query()` qui possède deux paramètres : `le nom du champ` contenant les données json, et le `json_path` vers la valeur a atteindre. Imaginons que nous voulions récupérer la deuxième valeur de la liste contenue dans "cle2" : + +```sql +SELECT + jsonb_path_query(donnee, '$.cle2[1]') +FROM test.test +``` + +Le `$` désigne le début du chemin json retourné. Nous faisons suivre ce premier symbole par un point pour passer à l'objet suivant puis par le nom de clé suivante et ainsi de suite juqu'à la clé recherchée, a laquelle nous collons un [1] pour la 2ème valeur de la liste (les valeurs commencent à 0). +Pour plus di'nformations sur les `json_path`, vous pouvez consulter la [documentation](https://www.postgresql.org/docs/current/functions-json.html#FUNCTIONS-SQLJSON-PATH) + +### Attaquons nous au recensement + +Bien, maintenant que nous avons essayé d'expliquer tant bien que mal les concepts avec des exemples simples car il faut bien commencer quelque part, essayons avec quelque chose d'un peu plus volumineux. + +Récupérons le dernier millésime du volet population du recensement communal [au format csv sur le site de l'INSEE](https://www.insee.fr/fr/information/8183122) (vous voulez les bases des principaux indicateurs). Pour l'exemple, on utilisera le fichier "Evolution et structure de la population". Il faut tout d'abord nettoyer les noms de champs car l'INSEE indique le millésime systématiquement dans ses noms de champs, ce qui fait que ces derniers changent tous les ans pour un même indicateur. + +Les noms de champs commencent tous par P ou C, ceci indique *exploitation principale* (réponses brutes aux questions du recensement) ou *exploitation complémentaire* (croisement de réponses pour établir un indicateur). Les champs provenant de l'exploitation principale et ceux issus de la complémentaire ne doivent pas êtres croisés entre eux. Cette information est évidemmment a conserver mais par choix personnel je préfère la mettre à la fin plutôt qu'au début du nom. + +Vous trouverez [ici](https://github.com/thomas-szczurek/base_donnees_insee/blob/main/sql/import/correction_champs_insee.xlsx) un tableur dont le rôle est de s'occuper de tout ça. + +- `STXT` est la fonction qui permet de découper une chaine de texte avec Excel, avec comme arguments `cellule comprenant la chaine initiale` , `position du début de la découpe` , `position de fin de la découpe` +- A partir du fichier récupéré, il vous suffit de copier coller les noms de champs INSEE sur la première ligne et de récupérer la seconde, qui contiendra les noms de champs formatés. +- On remplace les noms de champs du fichier INSEE original. +- Et on change le nom de cette table temportaire par "rp_population_import.csv" + +Avant d'insérer les données dans notre table json, nous allons passer par une table temporaire afin de rendre les données accessibles dans Postgres. Utiliser `COPY` de Postgresql serait fastidieux car il faudrai indiquer la centaine de champs que contient le volet population du recensement dans la commande. Et je n'ai pas honte de dire que j'ai un baobab dans la main à cette idée. Nous sortons donc ce merveilleux logiciel qu'est QGIS, on active les panneaux Explorateur et Explorateur2, on se créé une conection vers la base, et d'un mouvement gracile du poignet vous glissez le fichier depuis le panneau Explorateur vers la base Postgres dans l'Explorateur2. Laisser la magie opérer. + +Maintenant préparez vous pour peut-être un des INSERT les plus bizarre de votre vie (en tout ça la été pour moi !). Arf. Je me rend compte que si je veux bien faire il faut aussi que j'explique les CTE (c'est très étrange car encore une fois, si vous êtes encore ici vous savez très probablement déjà ce qu'est une CTE). + +CTE veut dire Common Table Expression. C'est fonctionnalité qui permet, grace à la clause `WITH` d'isoler une sous requête de la requête principale pour rendre tout un peu plus clair, ou de nommer la sous requête pour pouvoir la réutiliser à plusieurs endroits sans devoir la réécrire. On peut aussi s'en servir pour faire des requêtes récursives avec `WITH RECURSIVE` et si le sujet vous interesse je vous encurange à aller lire la [magnifique documentation](https://www.postgresql.org/docs/current/queries-with.html#QUERIES-WITH-RECURSIVE) de Postgres a ce sujet. + +On va utiliser la CTE pour concatenner le nom que l'on veut donner à nos clés avec les valeurs contenues dans notre table temporaire dans une chaine séparée par des `,`, qu'on enverra dans une fonction `string_to_array()` puis dans une fonction `jsonb_object()`. On en profiterra pour au passage nettoyer toute tabulation ou retour chariot qui pourrait subsister avec une expression régulière grace a la fonction `regex_replace()`. (ces caractères se notent `\t`, `\n` et `\r`). Cette dernière fonction prend 3 arguments : la chaine de caractère source, le `pattern` recherché, le texte de remplacement. On y ajoute le *drapeau* optionnel `g` afin de remplacer toutes les occurences trouvées. + +Notez que si votre table temporaire possède un nom différent de "rp_population_import" il vous faudra modifier la clause FROM de la CTE. + +```sql +-- cte concatenant les données avec les clés et nettoyant les caractères spéciaux. +WITH d AS ( + SELECT + "CODGEO", + regexp_replace('pop_p,' || "POP_P" || ', + pop_0_14_ans_p,' || "POP0014_P" || ', + pop_15_29_ans_p,' || "POP1529_P" || ', + pop_30_44_ans_p,' || "POP3044_P" || ', + pop_45_59_ans_p,' || "POP4559_P" || ', + pop_60_74_ans_p,' || "POP6074_P" || ', + pop_75_89_ans_p,' || "POP7589_P" || ', + pop_90_ans_plus_p,' || "POP90P_P" || ', + hommes_p,' || "POPH_P" || ', + hommes_0_14_ans_p,' || "H0014_P" || ', + hommes_15_29_ans_p,' || "H1529_P" || ', + hommes_30_44_ans_p,' || "H3044_P" || ', + hommes_45_59_ans_p,' || "H4559_P" || ', + hommes_60_74_ans_p,' || "H6074_P" || ', + hommes_75_89_ans_p,' || "H7589_P" || ', + hommes_90_ans_plus_p,' || "H90P_P" || ', + hommes_0_19_ans_p,' || "H0019_P" || ', + hommes_20_64_ans_p,' || "H2064_P" || ', + hommes_65_ans_plus_p,' || "H65P_P" || ', + femmes_p,' || "POPF_P" || ', + femmes_0_14_ans_p,' || "F0014_P" || ', + femmes_15_29_ans_p,' || "F1529_P" || ', + femmes_30_44_ans_p,' || "F3044_P" || ', + femmes_45_59_ans_p,' || "F4559_P" || ', + femmes_60_74_ans_p,' || "F6074_P" || ', + femmes_75_89_ans_p,' || "F7589_P" || ', + femmes_90_ans_plus_p,' || "F90P_P" || ', + femmes_0_19_ans_p,' || "F0019_P" || ', + femmes_20_64_ans_p,' || "F2064_P" || ', + femmes_65_ans_plus_p,' || "F65P_P" || ', + pop_1an_ou_plus_localisee_1an_auparavant_p,' || "POP01P_P" || ', + pop_1an_ou_plus_meme_logement_1an_auparavant_p,' || "POP01P_IRAN1_P" || ', + pop_1an_ou_plus_meme_commune_1an_auparavant_p,' || "POP01P_IRAN2_P" || ', + pop_1an_ou_plus_meme_departement_1an_auparavant_p,' || "POP01P_IRAN3_P" || ', + pop_1an_ou_plus_meme_region_1an_auparavant_p,' || "POP01P_IRAN4_P" || ', + pop_1an_ou_plus_autre_region_1an_auparavant_p,' || "POP01P_IRAN5_P" || ', + pop_1an_ou_plus_un_dom_1an_auparavant_p,' || "POP01P_IRAN6_P" || ', + pop_1an_ou_plus_hors_metropole_ou_dom_1an_auparavant_p,' || "POP01P_IRAN7_P" || ', + pop_1_14ans_autre_logement_1an_auparavant_p,' || "POP0114_IRAN2P_P" || ', + pop_1_14ans_meme_commune_1an_auparavant_p,' || "POP0114_IRAN2_P" || ', + pop_1_14ans_autre_commune_1an_auparavant_p,' || "POP0114_IRAN3P_P" || ', + pop_15_24ans_autre_logement_1an_auparavant_p,' || "POP1524_IRAN2P_P" || ', + pop_15_24ans_meme_commune_1an_auparavant_p,' || "POP1524_IRAN2_P" || ', + pop_15_24ans_autre_commune_1an_auparavant_p,' || "POP1524_IRAN3P_P" || ', + pop_25_54ans_autre_logement_1an_auparavant_p,' || "POP2554_IRAN2P_P" || ', + pop_25_54ans_meme_commune_1an_auparavant_p,' || "POP2554_IRAN2_P" || ', + pop_25_54ans_autre_commune_1an_auparavant_p,' || "POP2554_IRAN3P_P" || ', + pop_55_ou_plus_autre_logement_1an_auparavant_p,' || "POP55P_IRAN2P_P" || ', + pop_55_ou_plus_meme_commune_1an_auparavant_p,' || "POP55P_IRAN2_P" || ', + pop_55_ou_plus_autre_commune_1an_auparavant_p,' || "POP55P_IRAN3P_P" || ', + pop_15_ans_plus_c,' || "POP15P_C" || ', + agriculteurs_15_ans_plus_c,' || "POP15P_CS1_C" || ', + artisants_commercants_chefs_entreprise_15_ans_plus_c,' || "POP15P_CS2_C" || ', + cadres_prof_intel_sup_15_ans_plus_c,' || "POP15P_CS3_C" || ', + professions_intermediaires_15_ans_plus_c,' || "POP15P_CS4_C" || ', + employes_15_ans_plus_c,' || "POP15P_CS5_C" || ', + ouvriers_15_ans_plus_c,' || "POP15P_CS6_C" || ', + retraites_15_ans_plus_c,' || "POP15P_CS7_C" || ', + autres_15_ans_plus_c,' || "POP15P_CS8_C" || ', + hommes_15_ans_plus_c,' || "H15P_C" || ', + h_agriculteurs_15_ans_plus_c,' || "H15P_CS1_C" || ', + h_artisants_commercants_chefs_entreprise_15_ans_plus_c,' || "H15P_CS2_C" || ', + h_cadres_prof_intel_sup_15_ans_plus_c,' || "H15P_CS3_C" || ', + h_professions_intermediaires_15_ans_plus_c,' || "H15P_CS4_C" || ', + h_employes_15_ans_plus_c,' || "H15P_CS5_C" || ', + h_ouvriers_15_ans_plus_c,' || "H15P_CS6_C" || ', + h_retraites_15_ans_plus_c,' || "H15P_CS7_C" || ', + h_autres_15_ans_plus_c,' || "H15P_CS8_C" || ', + femmes_15_ans_plus_c,' || "F15P_C" || ', + f_agricultrices_15_ans_plus_c,' || "F15P_CS1_C" || ', + f_artisanes_commercantes_cheffes_entreprise_15_ans_plus_c,' || "F15P_CS2_C" || ', + f_cadres_prof_intel_sup_15_ans_plus_c,' || "F15P_CS3_C" || ', + f_professions_intermediaires_15_ans_plus_c,' || "F15P_CS4_C" || ', + f_employees_15_ans_plus_c,' || "F15P_CS5_C" || ', + f_ouvrieres_15_ans_plus_c,' || "F15P_CS6_C" || ', + f_retraitees_15_ans_plus_c,' || "F15P_CS7_C" || ', + f_autres_15_ans_plus_c,' || "F15P_CS8_C" || ', + population_15_24_ans_c,' || "POP1524_C" || ', + pop_15_24_ans_agriculteurs_c,' || "POP1524_CS1_C" || ', + pop_15_24_ans_artisants_commercants_chefs_entreprise_c,' || "POP1524_CS2_C" || ', + pop_15_24_ans_cadres_prof_intel_sup_c,' || "POP1524_CS3_C" || ', + pop_15_24_ans_professions_intermediaires_c,' || "POP1524_CS4_C" || ', + pop_15_24_ans_employes_c,' || "POP1524_CS5_C" || ', + pop_15_24_ans_ouvriers_c,' || "POP1524_CS6_C" || ', + pop_15_24_ans_retraites_c,' || "POP1524_CS7_C" || ', + pop_15_24_ans_autres_c,' || "POP1524_CS8_C" || ', + population_25_54_ans_c,' || "POP2554_C" || ', + pop_25_54_ans_agriculteurs_c,' || "POP2554_CS1_C" || ', + pop_25_54_ans_artisants_commercants_chefs_entreprise_c,' || "POP2554_CS2_C" || ', + pop_25_54_ans_cadres_prof_intel_sup_c,' || "POP2554_CS3_C" || ', + pop_25_54_ans_professions_intermediaires_c,' || "POP2554_CS4_C" || ', + pop_25_54_ans_employes_c,' || "POP2554_CS5_C" || ', + pop_25_54_ans_ouvriers_c,' || "POP2554_CS6_C" || ', + pop_25_54_ans_retraites_c,' || "POP2554_CS7_C" || ', + pop_25_54_ans_autres_c,' || "POP2554_CS8_C" || ', + population_55_ans_et_plus_c,' || "POP55P_C" || ', + pop_55_ans_et_plus_ans_agriculteurs_c,' || "POP55P_CS1_C" || ', + pop_55_ans_et_plus_ans_artisants_commercants_chefs_entreprise_c,' || "POP55P_CS2_C" || ', + pop_55_ans_et_plus_ans_cadres_prof_intel_sup_c,' || "POP55P_CS3_C" || ', + pop_55_ans_et_plus_ans_professions_intermediaires_c,' || "POP55P_CS4_C" || ', + pop_55_ans_et_plus_ans_employes_c,' || "POP55P_CS5_C" || ', + pop_55_ans_et_plus_ans_ouvriers_c,' || "POP55P_CS6_C" || ', + pop_55_ans_et_plus_ans_retraites_c,' || "POP55P_CS7_C" || ', + pop_55_ans_et_plus_ans_autres_c,' || "POP55P_CS8_C", + E'[\t\n\r]','','g') AS data + FROM insee.rp_population_import + ) + +-- Conversion des chaines de texte en json et insertion dans la table +INSERT INTO insee.donnees_communes("code_commune","annee","fk_base","donnees") +SELECT + "CODGEO", + 2021, + 1, + jsonb_object(string_to_array(data::text,',')) +FROM d +ORDER BY "CODGEO"; +``` + +Ouf. + +![donnees_communes](https://cdn.geotribu.fr/tinyfilemanager.php?p=articles-blog-rdp%2Farticles%2F2024%2Fpostgresql_json&view=donnees_communes.png){: .img-center loading=lazy } + +On veillera à bien grouper les inserts dans cet ordre annee/base/code_commune afin de faciliter la lecture des données par PostgreSQL (si votre table est partitionnée, celà sera facilité). + +Maintenant, imaginez que comme l'auteur de ces lignes, vos gros doigts boudinnés cafouillent et glissent sur les touches lors de la rédaction de cette requête, qu'une faute de frappe s'y glisse, et là c'est le drame. Comment modifier le nom d'une clé déjà encodée dans la table ? Avec cette astuce : + +```sql +CREATE TABLE example(id int PRIMARY KEY, champ jsonb); +INSERT INTO example VALUES + (1, '{"nme": "test"}'), + (2, '{"nme": "second test"}'); + +UPDATE example +SET champ = champ - 'nme' || jsonb_build_object('name', champ -> 'nme') +WHERE champ ? 'nme' +returning *; +``` + +`-` est un opérateur qui permet de supprimer une clé d'un objet json. Pour notre UPDATE on enlève donc notre faute de frappe de l'ensemble de l'objet et on concatène tout le reste avec la construction d'un nouvel objet où on corrige le nom de la clé, et à laquelle on assigne la valeur de la clé en train d'être supprimée (elle est toujours utilisable au moment de l'UPDATE), sur les champs qui la contiennent à l'origine avec `?`. + +### Et maintenant ? Qu'est-ce qu'on fait de ça ? + +Jusqu'à maintenant, nous n'avons travaillé qu'avec le volet population pour son dernier millésime. Imaginons maintenant que nous répétions l'exercice pour les 6 volets et sur plusieurs millésimes et ce sachant qu'au cours du temps, certains champs peuvent appraitre / disparaitre (changement dans les niveaux de diplômes observés par exemple). Il serait interessant de récupérer une table indiquant la première et la dernière année de présence de chacune des clés. Mettons que lors de ces travaux nous en avons profité pour mettre à jour une table "correspondance_clefs_champs" listant chacune des clés présente et son nom INSEE d'origine (tout du moins, celui que nous avions normalisé). + +```sql +CREATE MATERIALIZED VIEW insee.presence_clefs_annees AS +SELECT + c.pk_id AS pk_id, + c.clef_json AS clef_json, + c.fk_base AS fk_base, + p.premiere AS premiere_annee_presence, + d.derniere AS derniere_annee_presence +FROM insee.correspondance_clefs_champs AS c, + LATERAL (SELECT min(annee) AS premiere FROM insee.donnees_communes WHERE fk_base = c.fk_base AND donnees ? c.clef_json) AS p, + LATERAL (SELECT max(annee) AS derniere FROM insee.donnees_communes WHERE fk_base = c.fk_base AND donnees ? c.clef_json) AS d +ORDER BY fk_base, clef_json; +``` + +![vm_presence_cles_annees](https://cdn.geotribu.fr/tinyfilemanager.php?p=articles-blog-rdp%2Farticles%2F2024%2Fpostgresql_json&view=vm_presence_cles_annees.png){: .img-center loading=lazy } + +La seule difficulté ici est la présence de [LATERAL](https://docs.postgresql.fr/17/queries-table-expressions.html#QUERIES-FROM). Ce "mot" permet d'utiliser un champ de la requête principale dans une sous requête placée dans une clause FROM. Les éléments de la sous requête seront ensuite évalués atomiquement avant d'être joins à la table d'origine. Oui, ce n'est pas très facile à expliquer. Ici, le WHERE de la sous requête va interroger le champ données de la table "donnees_communes" pour voir si il y voit la clef_json actuellement en train d'être évaluée dans la table "correspondance_clefs_champs" possédant l'alias "c". Si oui, alors on prend la valeur minimum/maximum du champ année, et on le joint à cette ligne et uniquement cette ligne. Puis évaluation de clef_json suivante ... (*évalués atomiquement*) + +Maintenant, on voudrait proposer un produit un peu plus simple d'utilisation avec tout ça. Désolé d'avance, je vais copier une requête de 50 lignes une seconde fois. La seule table utilisée ici que nous n'avons pas créé est une table zonages_administratifs comprenant les codes communes dans un champ "code_admin", et un champ "fk_type" contenant le type de zonage administratif (1 pour les communes). + +```sql +CREATE MATERIALIZED VIEW insee.donnees_communes_olap AS +WITH +-- Sélection des codes communes + codes_com AS ( + SELECT + code_admin + FROM insee.zonages_administratifs + WHERE fk_type = 1 + ), +-- Sélections des clefs et unnest par année + clefs AS ( + SELECT + pk_id, + generate_series(premiere_annee_presence, derniere_annee_presence,1) AS annee + FROM insee.presence_clefs_annees + ), +-- cross join des clefs + année unnestées et des codes communaux + tc AS ( + SELECT + cc.code_admin AS code_com, + cl.annee AS annee, + cl.pk_id AS pk_id + FROM codes_com AS cc + CROSS JOIN clefs AS cl + ), +-- Selection finale avec récupération des données + final AS ( + SELECT + tc.code_com, + tc.annee, + co.fk_base, + co.clef_json, + CASE + WHEN (d.donnees ->> clef_json) IN ('','null','s','nd') THEN NULL + ELSE ((d.donnees ->> clef_json)::real) + END AS valeur + FROM tc + JOIN + insee.donnees_communes AS d ON (tc.code_com = d.code_commune AND tc.annee = d.annee) + LEFT JOIN + insee.presence_clefs_annees AS co ON tc.pk_id = co.pk_id + ORDER BY tc.annee, co.fk_base, co.clef_json, tc.code_com + ) +SELECT * FROM final WHERE valeur IS NOT NULL; +``` + +![vm_presence_cles_annees](https://cdn.geotribu.fr/tinyfilemanager.php?p=articles-blog-rdp%2Farticles%2F2024%2Fpostgresql_json&view=vm_donnees_olap.png){: .img-center loading=lazy } + +Attention, la création de cette vue matérialisée ou son refresh peut prendre un certain temps si vous avez stocké beaucoup de données (1 heure chez moi pour les 6 volets de 2015 à 2021). + +Enfin, histoire de vivre avec son temps et non comme un viel ours des cavernes ou comme un data machin de collectivité territoriale, on va convertir cette vue matérialisée en fichier [parquet](https://parquet.apache.org/). +Et pour ça on va utiliser gdal/ogr qui est décidemment incroyable. + +```sh +ogr2ogr -of parquet donnees_insee.parquet PG:"dbname='insee' shcema='insee' tables='donnees_communes_olap' user='nom_utilisateur' password='votre_mot_de_passe'" +``` + +Et on peut ainsi mettre le fichier sur un espace cloud, comme [ici](https://donnees-insee.s3.fr-par.scw.cloud/donnees_insee_olap.parquet) ! + + + +{% include "licenses/cc4_by-nc-sa.md" %} From 52002a6bece5677f155dad8f45b5eb6981eb54f0 Mon Sep 17 00:00:00 2001 From: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> Date: Tue, 31 Dec 2024 11:52:42 +0100 Subject: [PATCH 002/101] Update 2024-12-31_sql_json.md Signed-off-by: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> --- content/articles/2024/2024-12-31_sql_json.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/content/articles/2024/2024-12-31_sql_json.md b/content/articles/2024/2024-12-31_sql_json.md index 3b945a3353..e2737e2a61 100644 --- a/content/articles/2024/2024-12-31_sql_json.md +++ b/content/articles/2024/2024-12-31_sql_json.md @@ -440,7 +440,7 @@ ORDER BY "CODGEO"; Ouf. -![donnees_communes](https://cdn.geotribu.fr/tinyfilemanager.php?p=articles-blog-rdp%2Farticles%2F2024%2Fpostgresql_json&view=donnees_communes.png){: .img-center loading=lazy } +![donnees_communes](https://cdn.geotribu.fr/img/articles-blog-rdp/articles/2024/postgresql_json/donnees_communes.png){: .img-center loading=lazy } On veillera à bien grouper les inserts dans cet ordre annee/base/code_commune afin de faciliter la lecture des données par PostgreSQL (si votre table est partitionnée, celà sera facilité). @@ -478,7 +478,7 @@ FROM insee.correspondance_clefs_champs AS c, ORDER BY fk_base, clef_json; ``` -![vm_presence_cles_annees](https://cdn.geotribu.fr/tinyfilemanager.php?p=articles-blog-rdp%2Farticles%2F2024%2Fpostgresql_json&view=vm_presence_cles_annees.png){: .img-center loading=lazy } +![vm_presence_cles_annees](https://cdn.geotribu.fr/img/articles-blog-rdp/articles/2024/postgresql_json/vm_presence_cles_annees.png){: .img-center loading=lazy } La seule difficulté ici est la présence de [LATERAL](https://docs.postgresql.fr/17/queries-table-expressions.html#QUERIES-FROM). Ce "mot" permet d'utiliser un champ de la requête principale dans une sous requête placée dans une clause FROM. Les éléments de la sous requête seront ensuite évalués atomiquement avant d'être joins à la table d'origine. Oui, ce n'est pas très facile à expliquer. Ici, le WHERE de la sous requête va interroger le champ données de la table "donnees_communes" pour voir si il y voit la clef_json actuellement en train d'être évaluée dans la table "correspondance_clefs_champs" possédant l'alias "c". Si oui, alors on prend la valeur minimum/maximum du champ année, et on le joint à cette ligne et uniquement cette ligne. Puis évaluation de clef_json suivante ... (*évalués atomiquement*) @@ -531,7 +531,7 @@ WITH SELECT * FROM final WHERE valeur IS NOT NULL; ``` -![vm_presence_cles_annees](https://cdn.geotribu.fr/tinyfilemanager.php?p=articles-blog-rdp%2Farticles%2F2024%2Fpostgresql_json&view=vm_donnees_olap.png){: .img-center loading=lazy } +![vm_presence_cles_annees](https://cdn.geotribu.fr/img/articles-blog-rdp/articles/2024/postgresql_json/donnees_communes.png)vm_donnees_olap.png){: .img-center loading=lazy } Attention, la création de cette vue matérialisée ou son refresh peut prendre un certain temps si vous avez stocké beaucoup de données (1 heure chez moi pour les 6 volets de 2015 à 2021). From 77266de762870ef3e03a4b5b7f83cb0a1fe73d8a Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 31 Dec 2024 10:58:03 +0000 Subject: [PATCH 003/101] =?UTF-8?q?[pre-commit.ci]=20Corrections=20automat?= =?UTF-8?q?iques=20appliqu=C3=A9es=20par=20les=20git=20hooks.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- content/articles/2024/2024-12-31_sql_json.md | 353 +++++++++---------- 1 file changed, 176 insertions(+), 177 deletions(-) diff --git a/content/articles/2024/2024-12-31_sql_json.md b/content/articles/2024/2024-12-31_sql_json.md index e2737e2a61..e23c120fef 100644 --- a/content/articles/2024/2024-12-31_sql_json.md +++ b/content/articles/2024/2024-12-31_sql_json.md @@ -22,7 +22,7 @@ tags: Dans le cadre d'un projet personnel, j'ai voulu stocker une bonne partie des données du recensement de l'insee dans une base PostgreSQL avec des tables multimillésimes. Problème, même au sein d'un même jeu de données, les champs peuvent changer au cours des années et celà empêche de pouvoir dégager une structure de table fixe, ce qui est assez génant vous en conviendrez. La solution ? Passer par des données semi-structurées, soit stocker ces données en json dans le champ d'une table. Cet article se veut un condensé de cet expérience. !!! warning - Ces travaux ont été réalisés avant la sortie de PostgreSQL 17 qui ajoute d'importantes fonctionnalités pour le json comme les [`JSON_TABLE`](https://doc.postgresql.fr/17/functions-json.html#FUNCTIONS-SQLJSON-TABLE), elles ne seront ici pas évoquées. + Ces travaux ont été réalisés avant la sortie de PostgreSQL 17 qui ajoute d'importantes fonctionnalités pour le json comme les [`JSON_TABLE`](https://doc.postgresql.fr/17/functions-json.html#FUNCTIONS-SQLJSON-TABLE), elles ne seront ici pas évoquées. Puisque nous allons parler de json et de données semi-structurées, je me sens dans l'obligation de commencer cet article par un avertissement. @@ -108,7 +108,7 @@ PostgreSQL est capable de stocker les données/objets au format json dans des ch ### Les index -Il est possible d'indexer un champ de type `json` / `jsonb` sur ses clés **de premier niveau** et ça se fait avec des index de type `GIN`. +Il est possible d'indexer un champ de type `json` / `jsonb` sur ses clés **de premier niveau** et ça se fait avec des index de type `GIN`. ```sql CREATE INDEX idx_tb_champjson ON tb USING gin (champ_json); @@ -135,7 +135,7 @@ BEGIN -- table listant les differentes bases insee disponibles CREATE TABLE insee.bases ( -pk_id int2 PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY +pk_id int2 PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY nom text NOT NULL ); @@ -190,14 +190,14 @@ A quoi ressemblerai la création de insee.donnees_communes si on la partitionnai ```sql BEGIN CREATE TABLE insee.donnees_communes ( - pk_id int4 GENERATED BY DEFAULT AS IDENTITY, - code_commune text NOT NULL CHECK length(code_com) = 5, - annee int2 NOT NULL, - fk_base int2 NOT NULL, - donnees jsonb NOT NULL, - UNIQUE (annee, code_commune, fk_base), - CONSTRAINT pk_donnees_communes PRIMARY KEY (pk_id, fk_base), - CONSTRAINT fk_donnees_bases FOREIGN KEY (fk_base) REFERENCES insee.bases (pk_id) ON UPDATE CASCADE ON DELETE CASCADE + pk_id int4 GENERATED BY DEFAULT AS IDENTITY, + code_commune text NOT NULL CHECK length(code_com) = 5, + annee int2 NOT NULL, + fk_base int2 NOT NULL, + donnees jsonb NOT NULL, + UNIQUE (annee, code_commune, fk_base), + CONSTRAINT pk_donnees_communes PRIMARY KEY (pk_id, fk_base), + CONSTRAINT fk_donnees_bases FOREIGN KEY (fk_base) REFERENCES insee.bases (pk_id) ON UPDATE CASCADE ON DELETE CASCADE ) PARTITION BY LIST (fk_base); CREATE TABLE insee.donnees_communes_fk_1 PARTITION OF insee.donnees_communes FOR VALUES IN (1); CREATE TABLE insee.donnees_communes_fk_2 PARTITION OF insee.donnees_communes FOR VALUES IN (2); @@ -208,7 +208,6 @@ CREATE TABLE insee.donnees_communes_fk_6 PARTITION OF insee.donnees_communes FOR END; ``` - ### Insertion de données et récupération Pour passer des données textuelles / sql vers des données encodées en json dans le champ dédié qui va bien, PostgreSQL dispose de [*quelques* fonctions](https://docs.postgresql.fr/17/functions-json.html#FUNCTIONS-JSON-PROCESSING). Nous allons utiliser la fonction `jsonb_object()` qui permet de transformer un `array` sql sous forme `clef1, valeur1, clef2, valeur2 ....` en objet `jsonb` qui n'aura qu'un niveau d'imbrication. D'autres fonctions sont disponibles pour des objets plus complexes (comme `jsonb_build_object()`). @@ -264,11 +263,11 @@ Pour injecter du json complexe dans un champs, deux solutions s'offrent à nous ```sql INSERT INTO test.test (donnees) VALUES (jsonb_object( - 'cle1', 'valeur1', - 'cle2', jsonb_array( - 'foo', 'bar', 'baz') - ) - ) + 'cle1', 'valeur1', + 'cle2', jsonb_array( + 'foo', 'bar', 'baz') + ) + ) ``` Cette insertion pourrait tout aussi bien s'écrire avec un cast d'une chaine de texte vers du jsonb, attention, la syntaxe json doit être ici respectée : @@ -282,7 +281,7 @@ Pour récupérer une valeur, on utilise la fonction `jsonb_path_query()` qui pos ```sql SELECT - jsonb_path_query(donnee, '$.cle2[1]') + jsonb_path_query(donnee, '$.cle2[1]') FROM test.test ``` @@ -317,123 +316,123 @@ Notez que si votre table temporaire possède un nom différent de "rp_population ```sql -- cte concatenant les données avec les clés et nettoyant les caractères spéciaux. WITH d AS ( - SELECT - "CODGEO", - regexp_replace('pop_p,' || "POP_P" || ', - pop_0_14_ans_p,' || "POP0014_P" || ', - pop_15_29_ans_p,' || "POP1529_P" || ', - pop_30_44_ans_p,' || "POP3044_P" || ', - pop_45_59_ans_p,' || "POP4559_P" || ', - pop_60_74_ans_p,' || "POP6074_P" || ', - pop_75_89_ans_p,' || "POP7589_P" || ', - pop_90_ans_plus_p,' || "POP90P_P" || ', - hommes_p,' || "POPH_P" || ', - hommes_0_14_ans_p,' || "H0014_P" || ', - hommes_15_29_ans_p,' || "H1529_P" || ', - hommes_30_44_ans_p,' || "H3044_P" || ', - hommes_45_59_ans_p,' || "H4559_P" || ', - hommes_60_74_ans_p,' || "H6074_P" || ', - hommes_75_89_ans_p,' || "H7589_P" || ', - hommes_90_ans_plus_p,' || "H90P_P" || ', - hommes_0_19_ans_p,' || "H0019_P" || ', - hommes_20_64_ans_p,' || "H2064_P" || ', - hommes_65_ans_plus_p,' || "H65P_P" || ', - femmes_p,' || "POPF_P" || ', - femmes_0_14_ans_p,' || "F0014_P" || ', - femmes_15_29_ans_p,' || "F1529_P" || ', - femmes_30_44_ans_p,' || "F3044_P" || ', - femmes_45_59_ans_p,' || "F4559_P" || ', - femmes_60_74_ans_p,' || "F6074_P" || ', - femmes_75_89_ans_p,' || "F7589_P" || ', - femmes_90_ans_plus_p,' || "F90P_P" || ', - femmes_0_19_ans_p,' || "F0019_P" || ', - femmes_20_64_ans_p,' || "F2064_P" || ', - femmes_65_ans_plus_p,' || "F65P_P" || ', - pop_1an_ou_plus_localisee_1an_auparavant_p,' || "POP01P_P" || ', - pop_1an_ou_plus_meme_logement_1an_auparavant_p,' || "POP01P_IRAN1_P" || ', - pop_1an_ou_plus_meme_commune_1an_auparavant_p,' || "POP01P_IRAN2_P" || ', - pop_1an_ou_plus_meme_departement_1an_auparavant_p,' || "POP01P_IRAN3_P" || ', - pop_1an_ou_plus_meme_region_1an_auparavant_p,' || "POP01P_IRAN4_P" || ', - pop_1an_ou_plus_autre_region_1an_auparavant_p,' || "POP01P_IRAN5_P" || ', - pop_1an_ou_plus_un_dom_1an_auparavant_p,' || "POP01P_IRAN6_P" || ', - pop_1an_ou_plus_hors_metropole_ou_dom_1an_auparavant_p,' || "POP01P_IRAN7_P" || ', - pop_1_14ans_autre_logement_1an_auparavant_p,' || "POP0114_IRAN2P_P" || ', - pop_1_14ans_meme_commune_1an_auparavant_p,' || "POP0114_IRAN2_P" || ', - pop_1_14ans_autre_commune_1an_auparavant_p,' || "POP0114_IRAN3P_P" || ', - pop_15_24ans_autre_logement_1an_auparavant_p,' || "POP1524_IRAN2P_P" || ', - pop_15_24ans_meme_commune_1an_auparavant_p,' || "POP1524_IRAN2_P" || ', - pop_15_24ans_autre_commune_1an_auparavant_p,' || "POP1524_IRAN3P_P" || ', - pop_25_54ans_autre_logement_1an_auparavant_p,' || "POP2554_IRAN2P_P" || ', - pop_25_54ans_meme_commune_1an_auparavant_p,' || "POP2554_IRAN2_P" || ', - pop_25_54ans_autre_commune_1an_auparavant_p,' || "POP2554_IRAN3P_P" || ', - pop_55_ou_plus_autre_logement_1an_auparavant_p,' || "POP55P_IRAN2P_P" || ', - pop_55_ou_plus_meme_commune_1an_auparavant_p,' || "POP55P_IRAN2_P" || ', - pop_55_ou_plus_autre_commune_1an_auparavant_p,' || "POP55P_IRAN3P_P" || ', - pop_15_ans_plus_c,' || "POP15P_C" || ', - agriculteurs_15_ans_plus_c,' || "POP15P_CS1_C" || ', - artisants_commercants_chefs_entreprise_15_ans_plus_c,' || "POP15P_CS2_C" || ', - cadres_prof_intel_sup_15_ans_plus_c,' || "POP15P_CS3_C" || ', - professions_intermediaires_15_ans_plus_c,' || "POP15P_CS4_C" || ', - employes_15_ans_plus_c,' || "POP15P_CS5_C" || ', - ouvriers_15_ans_plus_c,' || "POP15P_CS6_C" || ', - retraites_15_ans_plus_c,' || "POP15P_CS7_C" || ', - autres_15_ans_plus_c,' || "POP15P_CS8_C" || ', - hommes_15_ans_plus_c,' || "H15P_C" || ', - h_agriculteurs_15_ans_plus_c,' || "H15P_CS1_C" || ', - h_artisants_commercants_chefs_entreprise_15_ans_plus_c,' || "H15P_CS2_C" || ', - h_cadres_prof_intel_sup_15_ans_plus_c,' || "H15P_CS3_C" || ', - h_professions_intermediaires_15_ans_plus_c,' || "H15P_CS4_C" || ', - h_employes_15_ans_plus_c,' || "H15P_CS5_C" || ', - h_ouvriers_15_ans_plus_c,' || "H15P_CS6_C" || ', - h_retraites_15_ans_plus_c,' || "H15P_CS7_C" || ', - h_autres_15_ans_plus_c,' || "H15P_CS8_C" || ', - femmes_15_ans_plus_c,' || "F15P_C" || ', - f_agricultrices_15_ans_plus_c,' || "F15P_CS1_C" || ', - f_artisanes_commercantes_cheffes_entreprise_15_ans_plus_c,' || "F15P_CS2_C" || ', - f_cadres_prof_intel_sup_15_ans_plus_c,' || "F15P_CS3_C" || ', - f_professions_intermediaires_15_ans_plus_c,' || "F15P_CS4_C" || ', - f_employees_15_ans_plus_c,' || "F15P_CS5_C" || ', - f_ouvrieres_15_ans_plus_c,' || "F15P_CS6_C" || ', - f_retraitees_15_ans_plus_c,' || "F15P_CS7_C" || ', - f_autres_15_ans_plus_c,' || "F15P_CS8_C" || ', - population_15_24_ans_c,' || "POP1524_C" || ', - pop_15_24_ans_agriculteurs_c,' || "POP1524_CS1_C" || ', - pop_15_24_ans_artisants_commercants_chefs_entreprise_c,' || "POP1524_CS2_C" || ', - pop_15_24_ans_cadres_prof_intel_sup_c,' || "POP1524_CS3_C" || ', - pop_15_24_ans_professions_intermediaires_c,' || "POP1524_CS4_C" || ', - pop_15_24_ans_employes_c,' || "POP1524_CS5_C" || ', - pop_15_24_ans_ouvriers_c,' || "POP1524_CS6_C" || ', - pop_15_24_ans_retraites_c,' || "POP1524_CS7_C" || ', - pop_15_24_ans_autres_c,' || "POP1524_CS8_C" || ', - population_25_54_ans_c,' || "POP2554_C" || ', - pop_25_54_ans_agriculteurs_c,' || "POP2554_CS1_C" || ', - pop_25_54_ans_artisants_commercants_chefs_entreprise_c,' || "POP2554_CS2_C" || ', - pop_25_54_ans_cadres_prof_intel_sup_c,' || "POP2554_CS3_C" || ', - pop_25_54_ans_professions_intermediaires_c,' || "POP2554_CS4_C" || ', - pop_25_54_ans_employes_c,' || "POP2554_CS5_C" || ', - pop_25_54_ans_ouvriers_c,' || "POP2554_CS6_C" || ', - pop_25_54_ans_retraites_c,' || "POP2554_CS7_C" || ', - pop_25_54_ans_autres_c,' || "POP2554_CS8_C" || ', - population_55_ans_et_plus_c,' || "POP55P_C" || ', - pop_55_ans_et_plus_ans_agriculteurs_c,' || "POP55P_CS1_C" || ', - pop_55_ans_et_plus_ans_artisants_commercants_chefs_entreprise_c,' || "POP55P_CS2_C" || ', - pop_55_ans_et_plus_ans_cadres_prof_intel_sup_c,' || "POP55P_CS3_C" || ', - pop_55_ans_et_plus_ans_professions_intermediaires_c,' || "POP55P_CS4_C" || ', - pop_55_ans_et_plus_ans_employes_c,' || "POP55P_CS5_C" || ', - pop_55_ans_et_plus_ans_ouvriers_c,' || "POP55P_CS6_C" || ', - pop_55_ans_et_plus_ans_retraites_c,' || "POP55P_CS7_C" || ', - pop_55_ans_et_plus_ans_autres_c,' || "POP55P_CS8_C", - E'[\t\n\r]','','g') AS data - FROM insee.rp_population_import - ) - --- Conversion des chaines de texte en json et insertion dans la table + SELECT + "CODGEO", + regexp_replace('pop_p,' || "POP_P" || ', + pop_0_14_ans_p,' || "POP0014_P" || ', + pop_15_29_ans_p,' || "POP1529_P" || ', + pop_30_44_ans_p,' || "POP3044_P" || ', + pop_45_59_ans_p,' || "POP4559_P" || ', + pop_60_74_ans_p,' || "POP6074_P" || ', + pop_75_89_ans_p,' || "POP7589_P" || ', + pop_90_ans_plus_p,' || "POP90P_P" || ', + hommes_p,' || "POPH_P" || ', + hommes_0_14_ans_p,' || "H0014_P" || ', + hommes_15_29_ans_p,' || "H1529_P" || ', + hommes_30_44_ans_p,' || "H3044_P" || ', + hommes_45_59_ans_p,' || "H4559_P" || ', + hommes_60_74_ans_p,' || "H6074_P" || ', + hommes_75_89_ans_p,' || "H7589_P" || ', + hommes_90_ans_plus_p,' || "H90P_P" || ', + hommes_0_19_ans_p,' || "H0019_P" || ', + hommes_20_64_ans_p,' || "H2064_P" || ', + hommes_65_ans_plus_p,' || "H65P_P" || ', + femmes_p,' || "POPF_P" || ', + femmes_0_14_ans_p,' || "F0014_P" || ', + femmes_15_29_ans_p,' || "F1529_P" || ', + femmes_30_44_ans_p,' || "F3044_P" || ', + femmes_45_59_ans_p,' || "F4559_P" || ', + femmes_60_74_ans_p,' || "F6074_P" || ', + femmes_75_89_ans_p,' || "F7589_P" || ', + femmes_90_ans_plus_p,' || "F90P_P" || ', + femmes_0_19_ans_p,' || "F0019_P" || ', + femmes_20_64_ans_p,' || "F2064_P" || ', + femmes_65_ans_plus_p,' || "F65P_P" || ', + pop_1an_ou_plus_localisee_1an_auparavant_p,' || "POP01P_P" || ', + pop_1an_ou_plus_meme_logement_1an_auparavant_p,' || "POP01P_IRAN1_P" || ', + pop_1an_ou_plus_meme_commune_1an_auparavant_p,' || "POP01P_IRAN2_P" || ', + pop_1an_ou_plus_meme_departement_1an_auparavant_p,' || "POP01P_IRAN3_P" || ', + pop_1an_ou_plus_meme_region_1an_auparavant_p,' || "POP01P_IRAN4_P" || ', + pop_1an_ou_plus_autre_region_1an_auparavant_p,' || "POP01P_IRAN5_P" || ', + pop_1an_ou_plus_un_dom_1an_auparavant_p,' || "POP01P_IRAN6_P" || ', + pop_1an_ou_plus_hors_metropole_ou_dom_1an_auparavant_p,' || "POP01P_IRAN7_P" || ', + pop_1_14ans_autre_logement_1an_auparavant_p,' || "POP0114_IRAN2P_P" || ', + pop_1_14ans_meme_commune_1an_auparavant_p,' || "POP0114_IRAN2_P" || ', + pop_1_14ans_autre_commune_1an_auparavant_p,' || "POP0114_IRAN3P_P" || ', + pop_15_24ans_autre_logement_1an_auparavant_p,' || "POP1524_IRAN2P_P" || ', + pop_15_24ans_meme_commune_1an_auparavant_p,' || "POP1524_IRAN2_P" || ', + pop_15_24ans_autre_commune_1an_auparavant_p,' || "POP1524_IRAN3P_P" || ', + pop_25_54ans_autre_logement_1an_auparavant_p,' || "POP2554_IRAN2P_P" || ', + pop_25_54ans_meme_commune_1an_auparavant_p,' || "POP2554_IRAN2_P" || ', + pop_25_54ans_autre_commune_1an_auparavant_p,' || "POP2554_IRAN3P_P" || ', + pop_55_ou_plus_autre_logement_1an_auparavant_p,' || "POP55P_IRAN2P_P" || ', + pop_55_ou_plus_meme_commune_1an_auparavant_p,' || "POP55P_IRAN2_P" || ', + pop_55_ou_plus_autre_commune_1an_auparavant_p,' || "POP55P_IRAN3P_P" || ', + pop_15_ans_plus_c,' || "POP15P_C" || ', + agriculteurs_15_ans_plus_c,' || "POP15P_CS1_C" || ', + artisants_commercants_chefs_entreprise_15_ans_plus_c,' || "POP15P_CS2_C" || ', + cadres_prof_intel_sup_15_ans_plus_c,' || "POP15P_CS3_C" || ', + professions_intermediaires_15_ans_plus_c,' || "POP15P_CS4_C" || ', + employes_15_ans_plus_c,' || "POP15P_CS5_C" || ', + ouvriers_15_ans_plus_c,' || "POP15P_CS6_C" || ', + retraites_15_ans_plus_c,' || "POP15P_CS7_C" || ', + autres_15_ans_plus_c,' || "POP15P_CS8_C" || ', + hommes_15_ans_plus_c,' || "H15P_C" || ', + h_agriculteurs_15_ans_plus_c,' || "H15P_CS1_C" || ', + h_artisants_commercants_chefs_entreprise_15_ans_plus_c,' || "H15P_CS2_C" || ', + h_cadres_prof_intel_sup_15_ans_plus_c,' || "H15P_CS3_C" || ', + h_professions_intermediaires_15_ans_plus_c,' || "H15P_CS4_C" || ', + h_employes_15_ans_plus_c,' || "H15P_CS5_C" || ', + h_ouvriers_15_ans_plus_c,' || "H15P_CS6_C" || ', + h_retraites_15_ans_plus_c,' || "H15P_CS7_C" || ', + h_autres_15_ans_plus_c,' || "H15P_CS8_C" || ', + femmes_15_ans_plus_c,' || "F15P_C" || ', + f_agricultrices_15_ans_plus_c,' || "F15P_CS1_C" || ', + f_artisanes_commercantes_cheffes_entreprise_15_ans_plus_c,' || "F15P_CS2_C" || ', + f_cadres_prof_intel_sup_15_ans_plus_c,' || "F15P_CS3_C" || ', + f_professions_intermediaires_15_ans_plus_c,' || "F15P_CS4_C" || ', + f_employees_15_ans_plus_c,' || "F15P_CS5_C" || ', + f_ouvrieres_15_ans_plus_c,' || "F15P_CS6_C" || ', + f_retraitees_15_ans_plus_c,' || "F15P_CS7_C" || ', + f_autres_15_ans_plus_c,' || "F15P_CS8_C" || ', + population_15_24_ans_c,' || "POP1524_C" || ', + pop_15_24_ans_agriculteurs_c,' || "POP1524_CS1_C" || ', + pop_15_24_ans_artisants_commercants_chefs_entreprise_c,' || "POP1524_CS2_C" || ', + pop_15_24_ans_cadres_prof_intel_sup_c,' || "POP1524_CS3_C" || ', + pop_15_24_ans_professions_intermediaires_c,' || "POP1524_CS4_C" || ', + pop_15_24_ans_employes_c,' || "POP1524_CS5_C" || ', + pop_15_24_ans_ouvriers_c,' || "POP1524_CS6_C" || ', + pop_15_24_ans_retraites_c,' || "POP1524_CS7_C" || ', + pop_15_24_ans_autres_c,' || "POP1524_CS8_C" || ', + population_25_54_ans_c,' || "POP2554_C" || ', + pop_25_54_ans_agriculteurs_c,' || "POP2554_CS1_C" || ', + pop_25_54_ans_artisants_commercants_chefs_entreprise_c,' || "POP2554_CS2_C" || ', + pop_25_54_ans_cadres_prof_intel_sup_c,' || "POP2554_CS3_C" || ', + pop_25_54_ans_professions_intermediaires_c,' || "POP2554_CS4_C" || ', + pop_25_54_ans_employes_c,' || "POP2554_CS5_C" || ', + pop_25_54_ans_ouvriers_c,' || "POP2554_CS6_C" || ', + pop_25_54_ans_retraites_c,' || "POP2554_CS7_C" || ', + pop_25_54_ans_autres_c,' || "POP2554_CS8_C" || ', + population_55_ans_et_plus_c,' || "POP55P_C" || ', + pop_55_ans_et_plus_ans_agriculteurs_c,' || "POP55P_CS1_C" || ', + pop_55_ans_et_plus_ans_artisants_commercants_chefs_entreprise_c,' || "POP55P_CS2_C" || ', + pop_55_ans_et_plus_ans_cadres_prof_intel_sup_c,' || "POP55P_CS3_C" || ', + pop_55_ans_et_plus_ans_professions_intermediaires_c,' || "POP55P_CS4_C" || ', + pop_55_ans_et_plus_ans_employes_c,' || "POP55P_CS5_C" || ', + pop_55_ans_et_plus_ans_ouvriers_c,' || "POP55P_CS6_C" || ', + pop_55_ans_et_plus_ans_retraites_c,' || "POP55P_CS7_C" || ', + pop_55_ans_et_plus_ans_autres_c,' || "POP55P_CS8_C", + E'[\t\n\r]','','g') AS data + FROM insee.rp_population_import + ) + +-- Conversion des chaines de texte en json et insertion dans la table INSERT INTO insee.donnees_communes("code_commune","annee","fk_base","donnees") SELECT - "CODGEO", - 2021, - 1, - jsonb_object(string_to_array(data::text,',')) + "CODGEO", + 2021, + 1, + jsonb_object(string_to_array(data::text,',')) FROM d ORDER BY "CODGEO"; ``` @@ -467,14 +466,14 @@ Jusqu'à maintenant, nous n'avons travaillé qu'avec le volet population pour so ```sql CREATE MATERIALIZED VIEW insee.presence_clefs_annees AS SELECT - c.pk_id AS pk_id, - c.clef_json AS clef_json, - c.fk_base AS fk_base, - p.premiere AS premiere_annee_presence, - d.derniere AS derniere_annee_presence + c.pk_id AS pk_id, + c.clef_json AS clef_json, + c.fk_base AS fk_base, + p.premiere AS premiere_annee_presence, + d.derniere AS derniere_annee_presence FROM insee.correspondance_clefs_champs AS c, - LATERAL (SELECT min(annee) AS premiere FROM insee.donnees_communes WHERE fk_base = c.fk_base AND donnees ? c.clef_json) AS p, - LATERAL (SELECT max(annee) AS derniere FROM insee.donnees_communes WHERE fk_base = c.fk_base AND donnees ? c.clef_json) AS d + LATERAL (SELECT min(annee) AS premiere FROM insee.donnees_communes WHERE fk_base = c.fk_base AND donnees ? c.clef_json) AS p, + LATERAL (SELECT max(annee) AS derniere FROM insee.donnees_communes WHERE fk_base = c.fk_base AND donnees ? c.clef_json) AS d ORDER BY fk_base, clef_json; ``` @@ -488,46 +487,46 @@ Maintenant, on voudrait proposer un produit un peu plus simple d'utilisation ave CREATE MATERIALIZED VIEW insee.donnees_communes_olap AS WITH -- Sélection des codes communes - codes_com AS ( - SELECT - code_admin - FROM insee.zonages_administratifs - WHERE fk_type = 1 - ), + codes_com AS ( + SELECT + code_admin + FROM insee.zonages_administratifs + WHERE fk_type = 1 + ), -- Sélections des clefs et unnest par année - clefs AS ( - SELECT - pk_id, - generate_series(premiere_annee_presence, derniere_annee_presence,1) AS annee - FROM insee.presence_clefs_annees - ), + clefs AS ( + SELECT + pk_id, + generate_series(premiere_annee_presence, derniere_annee_presence,1) AS annee + FROM insee.presence_clefs_annees + ), -- cross join des clefs + année unnestées et des codes communaux - tc AS ( - SELECT - cc.code_admin AS code_com, - cl.annee AS annee, - cl.pk_id AS pk_id - FROM codes_com AS cc - CROSS JOIN clefs AS cl - ), + tc AS ( + SELECT + cc.code_admin AS code_com, + cl.annee AS annee, + cl.pk_id AS pk_id + FROM codes_com AS cc + CROSS JOIN clefs AS cl + ), -- Selection finale avec récupération des données - final AS ( - SELECT - tc.code_com, - tc.annee, - co.fk_base, - co.clef_json, - CASE - WHEN (d.donnees ->> clef_json) IN ('','null','s','nd') THEN NULL - ELSE ((d.donnees ->> clef_json)::real) - END AS valeur - FROM tc - JOIN - insee.donnees_communes AS d ON (tc.code_com = d.code_commune AND tc.annee = d.annee) - LEFT JOIN - insee.presence_clefs_annees AS co ON tc.pk_id = co.pk_id - ORDER BY tc.annee, co.fk_base, co.clef_json, tc.code_com - ) + final AS ( + SELECT + tc.code_com, + tc.annee, + co.fk_base, + co.clef_json, + CASE + WHEN (d.donnees ->> clef_json) IN ('','null','s','nd') THEN NULL + ELSE ((d.donnees ->> clef_json)::real) + END AS valeur + FROM tc + JOIN + insee.donnees_communes AS d ON (tc.code_com = d.code_commune AND tc.annee = d.annee) + LEFT JOIN + insee.presence_clefs_annees AS co ON tc.pk_id = co.pk_id + ORDER BY tc.annee, co.fk_base, co.clef_json, tc.code_com + ) SELECT * FROM final WHERE valeur IS NOT NULL; ``` From 1cba9dd71fd7be2c7ef424d57eb42d1548961499 Mon Sep 17 00:00:00 2001 From: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> Date: Tue, 31 Dec 2024 12:11:25 +0100 Subject: [PATCH 004/101] Update 2024-12-31_sql_json.md correction d'un lien vers une image Signed-off-by: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> --- content/articles/2024/2024-12-31_sql_json.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/articles/2024/2024-12-31_sql_json.md b/content/articles/2024/2024-12-31_sql_json.md index e23c120fef..1e65db55bc 100644 --- a/content/articles/2024/2024-12-31_sql_json.md +++ b/content/articles/2024/2024-12-31_sql_json.md @@ -530,7 +530,7 @@ WITH SELECT * FROM final WHERE valeur IS NOT NULL; ``` -![vm_presence_cles_annees](https://cdn.geotribu.fr/img/articles-blog-rdp/articles/2024/postgresql_json/donnees_communes.png)vm_donnees_olap.png){: .img-center loading=lazy } +![vm_presence_cles_annees](https://cdn.geotribu.fr/img/articles-blog-rdp/articles/2024/postgresql_json/vm_donnees_olap.png)vm_donnees_olap.png){: .img-center loading=lazy } Attention, la création de cette vue matérialisée ou son refresh peut prendre un certain temps si vous avez stocké beaucoup de données (1 heure chez moi pour les 6 volets de 2015 à 2021). From ca37e7bcd9577ade3c8dfc3d2d8704759741def2 Mon Sep 17 00:00:00 2001 From: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> Date: Tue, 31 Dec 2024 12:18:16 +0100 Subject: [PATCH 005/101] Update 2024-12-31_sql_json.md corrections de quelques fautes de typo Signed-off-by: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> --- content/articles/2024/2024-12-31_sql_json.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/content/articles/2024/2024-12-31_sql_json.md b/content/articles/2024/2024-12-31_sql_json.md index 1e65db55bc..764375fa5b 100644 --- a/content/articles/2024/2024-12-31_sql_json.md +++ b/content/articles/2024/2024-12-31_sql_json.md @@ -1,4 +1,4 @@ ---- +:::--- title: "Travailler avec du json et PostgreSQL" subtitle: Jason et les éléphants authors: @@ -19,8 +19,7 @@ tags: ## Travailler avec du json dans PostgreSQL -Dans le cadre d'un projet personnel, j'ai voulu stocker une bonne partie des données du recensement de l'insee dans une base PostgreSQL avec des tables multimillésimes. Problème, même au sein d'un même jeu de données, les champs peuvent changer au cours des années et celà empêche de pouvoir dégager une structure de table fixe, ce qui est assez génant vous en conviendrez. La solution ? Passer par des données semi-structurées, soit stocker ces données en json dans le champ d'une table. Cet article se veut un condensé de cet expérience. - +Dans le cadre d'un projet personnel, j'ai voulu stocker une bonne partie des données du recensement de l'Insee dans une base PostgreSQL avec des tables multimillésimes. Problème, même au sein d'un même jeu de données, les champs peuvent changer au cours des années et celà empêche de pouvoir dégager une structure de table fixe, ce qui est assez génant vous en conviendrez. La solution ? Passer par des données semi-structurées, soit stocker ces données en json dans le champ d'une table. Cet article se veut un condensé de cette !!! warning Ces travaux ont été réalisés avant la sortie de PostgreSQL 17 qui ajoute d'importantes fonctionnalités pour le json comme les [`JSON_TABLE`](https://doc.postgresql.fr/17/functions-json.html#FUNCTIONS-SQLJSON-TABLE), elles ne seront ici pas évoquées. @@ -28,7 +27,7 @@ Puisque nous allons parler de json et de données semi-structurées, je me sens **Le modèle relationnel c'est bon, mangez en, et les contraintes d'intégrités ont été inventées pour de bonnes raisons.** -Cet article ne se veut surtout pas être une invitation à partir en mode yolo sur la gestion des données "c'est bon ya qu'a tout mettre en json" (comme un vulgaire dev qui mettrait tout dans mongodb diraient les mauvaises langues). +Cet article ne se veut surtout pas être une invitation à partir en mode yolo sur la gestion des données "c'est bon ya qu'a tout mettre en json" (comme un vulgaire dev qui mettrait tout dans Mongodb diraient les mauvaises langues). ### Le json pour les débutant.es From b5708c2318428404be0146262554be2b9a505dc3 Mon Sep 17 00:00:00 2001 From: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> Date: Tue, 31 Dec 2024 17:22:59 +0100 Subject: [PATCH 006/101] Update content/articles/2024/2024-12-31_sql_json.md Co-authored-by: Julien Signed-off-by: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> --- content/articles/2024/2024-12-31_sql_json.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/articles/2024/2024-12-31_sql_json.md b/content/articles/2024/2024-12-31_sql_json.md index 764375fa5b..920577794e 100644 --- a/content/articles/2024/2024-12-31_sql_json.md +++ b/content/articles/2024/2024-12-31_sql_json.md @@ -1,4 +1,4 @@ -:::--- +--- title: "Travailler avec du json et PostgreSQL" subtitle: Jason et les éléphants authors: From 810e6212e92da309eb602f959cb80c13987fa284 Mon Sep 17 00:00:00 2001 From: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> Date: Tue, 31 Dec 2024 17:23:39 +0100 Subject: [PATCH 007/101] Update content/articles/2024/2024-12-31_sql_json.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Je trouve le premier niveau de # trop gros à mon goût mais ça marche ! Co-authored-by: Julien Signed-off-by: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> --- content/articles/2024/2024-12-31_sql_json.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/articles/2024/2024-12-31_sql_json.md b/content/articles/2024/2024-12-31_sql_json.md index 920577794e..c94cef1eb6 100644 --- a/content/articles/2024/2024-12-31_sql_json.md +++ b/content/articles/2024/2024-12-31_sql_json.md @@ -17,7 +17,7 @@ tags: - json --- -## Travailler avec du json dans PostgreSQL +# Travailler avec du json dans PostgreSQL Dans le cadre d'un projet personnel, j'ai voulu stocker une bonne partie des données du recensement de l'Insee dans une base PostgreSQL avec des tables multimillésimes. Problème, même au sein d'un même jeu de données, les champs peuvent changer au cours des années et celà empêche de pouvoir dégager une structure de table fixe, ce qui est assez génant vous en conviendrez. La solution ? Passer par des données semi-structurées, soit stocker ces données en json dans le champ d'une table. Cet article se veut un condensé de cette !!! warning From 9d1e965b5802d05df7448b3541b7bb73fa959240 Mon Sep 17 00:00:00 2001 From: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> Date: Tue, 31 Dec 2024 17:23:47 +0100 Subject: [PATCH 008/101] Update content/articles/2024/2024-12-31_sql_json.md Co-authored-by: Julien Signed-off-by: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> --- content/articles/2024/2024-12-31_sql_json.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/articles/2024/2024-12-31_sql_json.md b/content/articles/2024/2024-12-31_sql_json.md index c94cef1eb6..e476290e27 100644 --- a/content/articles/2024/2024-12-31_sql_json.md +++ b/content/articles/2024/2024-12-31_sql_json.md @@ -1,5 +1,5 @@ --- -title: "Travailler avec du json et PostgreSQL" +title: Travailler avec du JSON et PostgreSQL subtitle: Jason et les éléphants authors: - Thomas SZCZUREK-GAYANT From caabce1a6b04aa90fe43547e455f623f3b20f748 Mon Sep 17 00:00:00 2001 From: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> Date: Tue, 31 Dec 2024 17:24:43 +0100 Subject: [PATCH 009/101] Update content/articles/2024/2024-12-31_sql_json.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Quoi ? l'équipe de Geotribu n'est pas prête à donner son corps à la science pour publier un article en un jour ? ;) Co-authored-by: Julien Signed-off-by: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> --- content/articles/2024/2024-12-31_sql_json.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/articles/2024/2024-12-31_sql_json.md b/content/articles/2024/2024-12-31_sql_json.md index e476290e27..6e92216c79 100644 --- a/content/articles/2024/2024-12-31_sql_json.md +++ b/content/articles/2024/2024-12-31_sql_json.md @@ -6,7 +6,7 @@ authors: categories: - article comments: true -date: 2024-12-31 +date: 2025-01-21 description: "Stocker des données au format json dans PostgreSQL, les consulter... et tout ça avec les données du recensement de l'INSEE pour l'exemple. " icon: simple/postgresql image: From bb62ff4bff9f7653deff8d9ffb5390f149dde8fa Mon Sep 17 00:00:00 2001 From: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> Date: Wed, 1 Jan 2025 16:30:04 +0100 Subject: [PATCH 010/101] Rename 2024-12-31_sql_json.md to 2025-01-21_travailler-avec-JSON-et-PostgreSQL.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit changement titre pour coller à la date de publication Signed-off-by: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> --- ...l_json.md => 2025-01-21_travailler-avec-JSON-et-PostgreSQL.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename content/articles/2024/{2024-12-31_sql_json.md => 2025-01-21_travailler-avec-JSON-et-PostgreSQL.md} (100%) diff --git a/content/articles/2024/2024-12-31_sql_json.md b/content/articles/2024/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md similarity index 100% rename from content/articles/2024/2024-12-31_sql_json.md rename to content/articles/2024/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md From 23ed9e626a7f6e2ba772809603a2d976d43f759b Mon Sep 17 00:00:00 2001 From: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> Date: Wed, 1 Jan 2025 16:52:55 +0100 Subject: [PATCH 011/101] Update 2025-01-21_travailler-avec-JSON-et-PostgreSQL.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Corrections de quelques fautes et ajout d'éclairicement et d'annecdotes que j'espère un peu rigolotes au passage Signed-off-by: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> --- ...1-21_travailler-avec-JSON-et-PostgreSQL.md | 38 ++++++++++--------- 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/content/articles/2024/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md b/content/articles/2024/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md index 6e92216c79..b58429d12c 100644 --- a/content/articles/2024/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md +++ b/content/articles/2024/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md @@ -19,9 +19,10 @@ tags: # Travailler avec du json dans PostgreSQL -Dans le cadre d'un projet personnel, j'ai voulu stocker une bonne partie des données du recensement de l'Insee dans une base PostgreSQL avec des tables multimillésimes. Problème, même au sein d'un même jeu de données, les champs peuvent changer au cours des années et celà empêche de pouvoir dégager une structure de table fixe, ce qui est assez génant vous en conviendrez. La solution ? Passer par des données semi-structurées, soit stocker ces données en json dans le champ d'une table. Cet article se veut un condensé de cette +Dans le cadre d'un projet personnel encadré de type "tu l'as fait pour les iris de Lille avec Sqlite, t'as vu que tu sais faire. Bon bah maintenant tu le fait pour toutes les communes de France dans PG", j'ai voulu stocker une bonne partie des données du recensement de l'Insee dans une base PostgreSQL avec des tables multimillésimes. Problème, même au sein d'un même jeu de données, les champs peuvent changer au cours des années et celà empêche de pouvoir dégager une structure de table fixe, ce qui est assez génant vous en conviendrez. La solution ? Passer par des données semi-structurées, soit stocker ces données en json dans le champ d'une table. Cet article se veut un condensé de cette expérience. + !!! warning - Ces travaux ont été réalisés avant la sortie de PostgreSQL 17 qui ajoute d'importantes fonctionnalités pour le json comme les [`JSON_TABLE`](https://doc.postgresql.fr/17/functions-json.html#FUNCTIONS-SQLJSON-TABLE), elles ne seront ici pas évoquées. + Ces travaux ont été réalisés avant la sortie de PostgreSQL 17 qui ajoute d'importantes fonctionnalités pour le json avec les [`JSON_TABLE`](https://doc.postgresql.fr/17/functions-json.html#FUNCTIONS-SQLJSON-TABLE), elles ne seront ici pas évoquées. Puisque nous allons parler de json et de données semi-structurées, je me sens dans l'obligation de commencer cet article par un avertissement. @@ -43,7 +44,7 @@ Pour celles et ceux qui ne connaisent pas le `json`, il s'agit d'un format textu {"nb_champignons": 42, "nb_tomates": 31, "prenom": "roger"} ``` -Les valeurs peuvent prendre deux formes. Soit une valeur unique comme dans l'exemple ci-dessus, soit un `array` (une liste), les deux pouvant êtres combinés au sein d'un seul objet json. +Les valeurs peuvent prendre deux formes. Soit une valeur unique comme dans l'exemple ci-dessus, soit un `array` (une liste) qu'on place entre `[]`, les deux pouvant êtres combinés au sein d'un seul objet json. ```json {"prenoms": ["elodie", "roger", "fatima"], "nb_champgnons": 42} @@ -130,11 +131,11 @@ Je vais éviter de vous spammer du DDL, mais vous pourrez retrouver le schéma c Partant d'un schéma nommé `insee`, on va créer deux tables. La première contiendra la liste des *bases* disponibles (les différentes volets du recensement) et une seconde permettant de stocker les données (pour rester concentré sur le json, on va s'épargner 95% du modèle sous jacent, on ne gérera donc par ici les codes communes etc...). En bonus pour celles et ceux voulant tester ce que Postgres a dans le ventre, je proposerai une variante juste en dessous : ```sql -BEGIN +BEGIN; -- table listant les differentes bases insee disponibles CREATE TABLE insee.bases ( -pk_id int2 PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY +pk_id int2 PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, nom text NOT NULL ); @@ -142,7 +143,7 @@ nom text NOT NULL CREATE TABLE insee.donnees_communes ( pk_id int4 PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, -code_commune text NOT NULL CHECK length(code_com) = 5, -- n'oublions pas les Corses qui ont des codes commune en 2A et 2B d'où le type text +code_commune text NOT NULL CHECK length(code_commune) = 5, -- n'oublions pas les Corses qui ont des codes commune en 2A et 2B d'où le type text fk_base int2 NOT NULL, annee int2 NOT NULL, donnees jsonb NOT NULL, @@ -151,12 +152,12 @@ CONSTRAINT fk_donnees_bases FOREIGN KEY fk_base ON insee.bases(pk_id) ON UPDATE ); -- Création des index --- Un index multichamps est créé automatiquement sur (fk_base, annee, code_commune) grace à la clause UNIQUE +-- Un index multichamps est déjà créé automatiquement sur (fk_base, annee, code_commune) grace à la clause UNIQUE de la déclaration de table CREATE INDEX idx_donnees_com_donnees ON insee.donnees_communes USING gin (donnees); END; ``` -Les plus tatillons des dba d'entre vous auront remarqués ce `CHECK`. "Mais pourquoi qu'il utilise pas varchar(5), il fait vraiment n'importe quoi". Tout simplement car cette forme permet d'utiliser un type de texte aux nombre de caractères **réellement** arbitraire (le type `text`) contrairement à varchar(256), tout en pouvant en contrôler le nombre minimum et maximum avec le prédicat `CHECK` (contrairement à varchar qui ne contrôle que le maximum) comme expliqué sur le [wiki Postgres](https://wiki.postgresql.org/wiki/Don%27t_Do_This#Don.27t_use_varchar.28n.29_by_default). +Les plus tatillons des dba d'entre vous auront remarqués ce `CHECK`. "Mais pourquoi qu'il utilise pas varchar(5), il fait vraiment n'importe quoi". Tout simplement car cette forme permet d'utiliser un type de texte aux nombre de caractères **réellement** arbitraire (le type `text`) contrairement à varchar(255), tout en pouvant en contrôler le nombre minimum et maximum avec le prédicat `CHECK` (contrairement à varchar qui ne contrôle que le maximum) comme expliqué sur le [wiki Postgres](https://wiki.postgresql.org/wiki/Don%27t_Do_This#Don.27t_use_varchar.28n.29_by_default). Et on insère quleques ligne dans notre table insee.bases @@ -187,10 +188,10 @@ Imaginons que nous travaillons sur l'ensemble des données du recensement (soit A quoi ressemblerai la création de insee.donnees_communes si on la partitionnait selon les différentes sources de données ? ```sql -BEGIN +BEGIN; CREATE TABLE insee.donnees_communes ( pk_id int4 GENERATED BY DEFAULT AS IDENTITY, - code_commune text NOT NULL CHECK length(code_com) = 5, + code_commune text NOT NULL CHECK length(code_commune) = 5, annee int2 NOT NULL, fk_base int2 NOT NULL, donnees jsonb NOT NULL, @@ -206,6 +207,7 @@ CREATE TABLE insee.donnees_communes_fk_5 PARTITION OF insee.donnees_communes FOR CREATE TABLE insee.donnees_communes_fk_6 PARTITION OF insee.donnees_communes FOR VALUES IN (6); END; ``` +On remarque quelques changements : la déclaration de la clé primaire composite dans une `CONSTRAINT`, le `PARTITION BY LIST` qu'on fait suivre par le champ de partitionnement, et la création des différentes partitions suivant les valeurs de ce champ. ### Insertion de données et récupération @@ -249,15 +251,15 @@ WHERE donnees ? 'melons' ``` -Vous pouvez voir que j'utilise l'opérateur `?` (uniquement vable pour les champs `jsonb` et non ceux en simple `json`). En effet, lorsque l'on requête un champ json/jsonb, un retour vous est fait pour l'ensemble les enregistrements de la table, même ceux ne contenant pas la clé. Comprendre que si votre table comprends 100 000 enregistrements, mais que seuls 100 contiennent la clé "melons", ne pas spécifier cette clause WHERE vous renverait 100 000 lignes, dont 99 900 de `NULL`.`?` est un opérateur json permettant de poser la question "la clé est-elle présente au premier niveau du champ json pour cet enregistrement ?", et on ne récupérerai que nos 100 enregistrements contenant la clé "melons". +Vous pouvez voir que j'utilise l'opérateur `?` (uniquement vable pour les champs `jsonb` et non ceux en simple `json`). En effet, lorsque l'on requête un champ json/jsonb, un retour vous est fait pour l'ensemble les enregistrements de la table, même ceux ne contenant pas la clé. Comprendre que si votre table comprends 100 000 enregistrements, mais que seuls 100 contiennent la clé "melons", ne pas spécifier cette clause WHERE vous renverait 100 000 lignes, dont 99 900 de `NULL`.`?` est un opérateur json permettant de poser la question "la clé est-elle présente au premier niveau du champ json pour cet enregistrement ?", et on ne récupérerait que nos 100 enregistrements contenant la clé "melons". -Je précise, même si, si vous êtes encore ici je suppose que vous savez déjà cela, mais la forme `(quelque_chose)::int4` est un raccourcis de PostgreSQL pour faire un `cast` soit convertir une valeur dans un autre type. Avec `->>` la valeur nous est renvoyée sour forme de texte et nous la convertissons en entier. +Je précise, même si si vous êtes encore ici je suppose que vous savez déjà cela, mais la forme `(quelque_chose)::int4` est un raccourci de PostgreSQL pour faire un `cast` soit convertir une valeur dans un autre type. Avec `->>` la valeur nous est renvoyée sour forme de texte et nous la convertissons en entier. #### Avec du json imbriqué Bon, il est bien gentil, mais là son json reste du json où tout est au premier niveau. Bah oui, pour mon besoin cela m'a suffit. Mais je ne vais pas fuir mes responsabilités et on va voir comment cela se passe avec du json plus complexe. Je repartirai de l'exemple de la partie précedente pour compléxifier après cet apparté. -Pour injecter du json complexe dans un champs, deux solutions s'offrent à nous : Imbriquer les fonctions dédiées, ou caster une chaine de texte. Imaginons un table "test", dans un schema "test" et dont un champ `jsonb` se nomme "données". +Pour injecter du json complexe dans un champs, deux solutions s'offrent à nous : imbriquer les fonctions dédiées, ou caster une chaine de texte. Imaginons un table "test", dans un schema "test" et dont un champ `jsonb` se nomme "donnees". ```sql INSERT INTO test.test (donnees) VALUES @@ -302,11 +304,11 @@ Vous trouverez [ici](https://github.com/thomas-szczurek/base_donnees_insee/blob/ - On remplace les noms de champs du fichier INSEE original. - Et on change le nom de cette table temportaire par "rp_population_import.csv" -Avant d'insérer les données dans notre table json, nous allons passer par une table temporaire afin de rendre les données accessibles dans Postgres. Utiliser `COPY` de Postgresql serait fastidieux car il faudrai indiquer la centaine de champs que contient le volet population du recensement dans la commande. Et je n'ai pas honte de dire que j'ai un baobab dans la main à cette idée. Nous sortons donc ce merveilleux logiciel qu'est QGIS, on active les panneaux Explorateur et Explorateur2, on se créé une conection vers la base, et d'un mouvement gracile du poignet vous glissez le fichier depuis le panneau Explorateur vers la base Postgres dans l'Explorateur2. Laisser la magie opérer. +Avant d'insérer les données dans notre table, nous allons passer par une table temporaire afin de rendre les données accessibles dans Postgres. Utiliser `COPY` de Postgresql serait fastidieux car il faudrait indiquer la centaine de champs que contient le volet population du recensement dans la commande. Et je n'ai pas honte de dire que j'ai un baobab dans la main à cette idée. Nous sortons donc ce merveilleux logiciel qu'est QGIS, on active les panneaux Explorateur et Explorateur2, on se créé une conection vers la base avec les droits de création, et d'un mouvement gracile du poignet vous glissez le fichier depuis le panneau Explorateur vers la base Postgres dans l'Explorateur2. Laisser la magie opérer. Maintenant préparez vous pour peut-être un des INSERT les plus bizarre de votre vie (en tout ça la été pour moi !). Arf. Je me rend compte que si je veux bien faire il faut aussi que j'explique les CTE (c'est très étrange car encore une fois, si vous êtes encore ici vous savez très probablement déjà ce qu'est une CTE). -CTE veut dire Common Table Expression. C'est fonctionnalité qui permet, grace à la clause `WITH` d'isoler une sous requête de la requête principale pour rendre tout un peu plus clair, ou de nommer la sous requête pour pouvoir la réutiliser à plusieurs endroits sans devoir la réécrire. On peut aussi s'en servir pour faire des requêtes récursives avec `WITH RECURSIVE` et si le sujet vous interesse je vous encurange à aller lire la [magnifique documentation](https://www.postgresql.org/docs/current/queries-with.html#QUERIES-WITH-RECURSIVE) de Postgres a ce sujet. +CTE veut dire Common Table Expression. C'est une fonctionnalité qui permet, grace à la clause `WITH` d'isoler une sous requête de la requête principale pour rendre tout un peu plus clair, ou de la nommer pour pouvoir la réutiliser à plusieurs endroits sans devoir la réécrire. On peut aussi s'en servir pour faire des requêtes récursives avec `WITH RECURSIVE` et si le sujet vous interesse je vous encurange à aller lire la [magnifique documentation](https://www.postgresql.org/docs/current/queries-with.html#QUERIES-WITH-RECURSIVE) de Postgres a ce sujet. On va utiliser la CTE pour concatenner le nom que l'on veut donner à nos clés avec les valeurs contenues dans notre table temporaire dans une chaine séparée par des `,`, qu'on enverra dans une fonction `string_to_array()` puis dans une fonction `jsonb_object()`. On en profiterra pour au passage nettoyer toute tabulation ou retour chariot qui pourrait subsister avec une expression régulière grace a la fonction `regex_replace()`. (ces caractères se notent `\t`, `\n` et `\r`). Cette dernière fonction prend 3 arguments : la chaine de caractère source, le `pattern` recherché, le texte de remplacement. On y ajoute le *drapeau* optionnel `g` afin de remplacer toutes les occurences trouvées. @@ -460,7 +462,7 @@ returning *; ### Et maintenant ? Qu'est-ce qu'on fait de ça ? -Jusqu'à maintenant, nous n'avons travaillé qu'avec le volet population pour son dernier millésime. Imaginons maintenant que nous répétions l'exercice pour les 6 volets et sur plusieurs millésimes et ce sachant qu'au cours du temps, certains champs peuvent appraitre / disparaitre (changement dans les niveaux de diplômes observés par exemple). Il serait interessant de récupérer une table indiquant la première et la dernière année de présence de chacune des clés. Mettons que lors de ces travaux nous en avons profité pour mettre à jour une table "correspondance_clefs_champs" listant chacune des clés présente et son nom INSEE d'origine (tout du moins, celui que nous avions normalisé). +Jusqu'à maintenant, nous n'avons travaillé qu'avec le volet population pour son dernier millésime. Imaginons maintenant que nous répétions l'exercice pour les 6 volets et sur plusieurs millésimes et ce sachant qu'au cours du temps, certains champs peuvent apparaitre / disparaitre (changement dans les niveaux de diplômes observés par exemple). Il serait interessant de récupérer une table indiquant la première et la dernière année de présence de chacune des clés. Mettons que lors de ces travaux nous en avons profité pour mettre à jour une table "correspondance_clefs_champs" listant chacune des clés présente et son nom INSEE d'origine (tout du moins, celui que nous avions normalisé). ```sql CREATE MATERIALIZED VIEW insee.presence_clefs_annees AS @@ -533,14 +535,14 @@ SELECT * FROM final WHERE valeur IS NOT NULL; Attention, la création de cette vue matérialisée ou son refresh peut prendre un certain temps si vous avez stocké beaucoup de données (1 heure chez moi pour les 6 volets de 2015 à 2021). -Enfin, histoire de vivre avec son temps et non comme un viel ours des cavernes ou comme un data machin de collectivité territoriale, on va convertir cette vue matérialisée en fichier [parquet](https://parquet.apache.org/). +Enfin, histoire de vivre avec son temps et non comme un viel ours des cavernes ou comme un data machin de collectivité territoriale (j'y bosse, j'ai le droit), on va convertir cette vue matérialisée en fichier [parquet](https://parquet.apache.org/). Et pour ça on va utiliser gdal/ogr qui est décidemment incroyable. ```sh ogr2ogr -of parquet donnees_insee.parquet PG:"dbname='insee' shcema='insee' tables='donnees_communes_olap' user='nom_utilisateur' password='votre_mot_de_passe'" ``` -Et on peut ainsi mettre le fichier sur un espace cloud, comme [ici](https://donnees-insee.s3.fr-par.scw.cloud/donnees_insee_olap.parquet) ! +Et on peut ainsi mettre le fichier sur un espace cloud, comme [ici](https://donnees-insee.s3.fr-par.scw.cloud/donnees_insee_olap.parquet) ! Vous pouvez ensuite sortir votre plus beau générateur de publications Linkedin qui mettra plein d'emojis choupi et faire le cake sur les rezos (imaginez que 90% du contenu de Linkedin doit être fait avec ces trucs qui sont capable de vous générer des publications expliquant que le shape a des avantages sur le geopackage, le tout sur un ton très assuré). From ac82b2ef1f1e3e8b05a952a35e90e22353c092c6 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 1 Jan 2025 15:53:24 +0000 Subject: [PATCH 012/101] =?UTF-8?q?[pre-commit.ci]=20Corrections=20automat?= =?UTF-8?q?iques=20appliqu=C3=A9es=20par=20les=20git=20hooks.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../2024/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md | 1 + 1 file changed, 1 insertion(+) diff --git a/content/articles/2024/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md b/content/articles/2024/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md index b58429d12c..523a72ce31 100644 --- a/content/articles/2024/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md +++ b/content/articles/2024/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md @@ -207,6 +207,7 @@ CREATE TABLE insee.donnees_communes_fk_5 PARTITION OF insee.donnees_communes FOR CREATE TABLE insee.donnees_communes_fk_6 PARTITION OF insee.donnees_communes FOR VALUES IN (6); END; ``` + On remarque quelques changements : la déclaration de la clé primaire composite dans une `CONSTRAINT`, le `PARTITION BY LIST` qu'on fait suivre par le champ de partitionnement, et la création des différentes partitions suivant les valeurs de ce champ. ### Insertion de données et récupération From ad5ea40ad74877a7e9e1859528b6f359e5702ad2 Mon Sep 17 00:00:00 2001 From: GeoJulien Date: Wed, 1 Jan 2025 17:20:57 +0100 Subject: [PATCH 013/101] temp: disable RDP pages for 2025 --- content/rdp/.pages | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/rdp/.pages b/content/rdp/.pages index 1c5f0e9842..97af18fcc2 100644 --- a/content/rdp/.pages +++ b/content/rdp/.pages @@ -1,7 +1,7 @@ title: "📰 Revues de presse" nav: - - "2025" + # - "2025" - "2024" - "2023" - "2022" From 7ec027ee39ded27b91ffa7fe8b76978bae81aeeb Mon Sep 17 00:00:00 2001 From: GeoJulien Date: Wed, 1 Jan 2025 17:25:06 +0100 Subject: [PATCH 014/101] fix(json_postgresql): rapides corrections de syntaxe --- ...1-21_travailler-avec-JSON-et-PostgreSQL.md | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/content/articles/2024/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md b/content/articles/2024/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md index 523a72ce31..60d30b38e8 100644 --- a/content/articles/2024/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md +++ b/content/articles/2024/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md @@ -17,12 +17,12 @@ tags: - json --- -# Travailler avec du json dans PostgreSQL +# Travailler avec du JSON dans PostgreSQL -Dans le cadre d'un projet personnel encadré de type "tu l'as fait pour les iris de Lille avec Sqlite, t'as vu que tu sais faire. Bon bah maintenant tu le fait pour toutes les communes de France dans PG", j'ai voulu stocker une bonne partie des données du recensement de l'Insee dans une base PostgreSQL avec des tables multimillésimes. Problème, même au sein d'un même jeu de données, les champs peuvent changer au cours des années et celà empêche de pouvoir dégager une structure de table fixe, ce qui est assez génant vous en conviendrez. La solution ? Passer par des données semi-structurées, soit stocker ces données en json dans le champ d'une table. Cet article se veut un condensé de cette expérience. +Dans le cadre d'un projet personnel encadré de type "tu l'as fait pour les iris de Lille avec Sqlite, t'as vu que tu sais faire. Bon bah maintenant tu le fait pour toutes les communes de France dans PG", j'ai voulu stocker une bonne partie des données du recensement de l'Insee dans une base PostgreSQL avec des tables multimillésimes. Problème, même au sein d'un même jeu de données, les champs peuvent changer au cours des années et celà empêche de pouvoir dégager une structure de table fixe, ce qui est assez génant vous en conviendrez. La solution ? Passer par des données semi-structurées, soit stocker ces données en JSON dans le champ d'une table. Cet article se veut un condensé de cette expérience. !!! warning - Ces travaux ont été réalisés avant la sortie de PostgreSQL 17 qui ajoute d'importantes fonctionnalités pour le json avec les [`JSON_TABLE`](https://doc.postgresql.fr/17/functions-json.html#FUNCTIONS-SQLJSON-TABLE), elles ne seront ici pas évoquées. + Ces travaux ont été réalisés avant la sortie de PostgreSQL 17 qui ajoute d'importantes fonctionnalités pour le JSON avec les [`JSON_TABLE`](https://doc.postgresql.fr/17/functions-json.html#FUNCTIONS-SQLJSON-TABLE), elles ne seront pas évoquées ici. Puisque nous allons parler de json et de données semi-structurées, je me sens dans l'obligation de commencer cet article par un avertissement. @@ -30,7 +30,7 @@ Puisque nous allons parler de json et de données semi-structurées, je me sens Cet article ne se veut surtout pas être une invitation à partir en mode yolo sur la gestion des données "c'est bon ya qu'a tout mettre en json" (comme un vulgaire dev qui mettrait tout dans Mongodb diraient les mauvaises langues). -### Le json pour les débutant.es +## Le JSON pour les débutant.es Pour celles et ceux qui ne connaisent pas le `json`, il s'agit d'un format textuel de représentation des données fonctionnant en partie sur un système de `clé : valeur` qu'on peut voir comme une sorte d'évolution du `xml`. @@ -98,7 +98,7 @@ Ce qu'on appelle un objet, c'est tout ce qu'il se trouve entre les `{}` qui serv Exemple issu du site de [Mozilla](https://developer.mozilla.org/fr/docs/Learn/JavaScript/Objects/JSON) qui vous permettra d'approfondir. Vous pouvez aussi consulter l'article [wikipedia](https://fr.wikipedia.org/wiki/JavaScript_Object_Notation) ou encore [l'infâme site de la spécification](https://www.json.org/json-fr.html). -### Les types json de PostgreSQL +## Les types json de PostgreSQL PostgreSQL est capable de stocker les données/objets au format json dans des champs auxquels on attribuera un type dédié. Il y en a deux car sinon ça ne serait pas rigolo. @@ -106,7 +106,7 @@ PostgreSQL est capable de stocker les données/objets au format json dans des ch - le type `jsonb`. Le type moderne. Il stocke les informations sous forme binaire et énormément de fonctions sont disponibles en plus de celles du type `json`. -### Les index +## Les index Il est possible d'indexer un champ de type `json` / `jsonb` sur ses clés **de premier niveau** et ça se fait avec des index de type `GIN`. @@ -124,7 +124,7 @@ CREATE INDEX idx_tb_champjson ON tb USING gin (champ_json -> cle_ou_se_situent_l Pour les aventuriers et aventurières, il existe une extention de PostgreSQL `btree_gin` permettant de faire des index multi-champs mixant des champs classiques et json. Elle est disponible nativement à l'installation de PostgreSQL et ne vous demandera pas de devenir développeur.se C/C++ pour l'installer (coucou les fdw parquet ! Ca va par chez vous ?). -### Création des tables +## Création des tables Je vais éviter de vous spammer du DDL, mais vous pourrez retrouver le schéma complet de la base [ici](https://github.com/thomas-szczurek/base_donnees_insee/tree/main/sql/creation_tables) @@ -171,7 +171,7 @@ INSERT INTO insee.bases (nom) VALUES ('rp_emploi') ``` -#### (Bonus) Partition de la table données +### (Bonus) Partition de la table données Imaginons que nous travaillons sur l'ensemble des données du recensement (soit 6 fichiers sources), de 2015 à 2021 pour environ 35 000 communes. On va arriver sur du 1,5 millions d'enregistrements. Entendons nous bien, à l'échelle de Postgres ça ne reste pas grand chose. Mais, si comme moi vous voulez voir ce qu'on peut tirer des entrailles de Postres, ça permet de pouvoir commencer à justifier d'utiliser certaines fonctionnalités avancées. Une table partitionnée c'est quoi ? @@ -210,11 +210,11 @@ END; On remarque quelques changements : la déclaration de la clé primaire composite dans une `CONSTRAINT`, le `PARTITION BY LIST` qu'on fait suivre par le champ de partitionnement, et la création des différentes partitions suivant les valeurs de ce champ. -### Insertion de données et récupération +## Insertion de données et récupération Pour passer des données textuelles / sql vers des données encodées en json dans le champ dédié qui va bien, PostgreSQL dispose de [*quelques* fonctions](https://docs.postgresql.fr/17/functions-json.html#FUNCTIONS-JSON-PROCESSING). Nous allons utiliser la fonction `jsonb_object()` qui permet de transformer un `array` sql sous forme `clef1, valeur1, clef2, valeur2 ....` en objet `jsonb` qui n'aura qu'un niveau d'imbrication. D'autres fonctions sont disponibles pour des objets plus complexes (comme `jsonb_build_object()`). -#### Exemple simple +### Exemple simple On va créer une chaine de texte qui contiendra le contenu de notre json dont les valeurs serons séparées par des virgules `clé1,valeur1, clé2, valeur2`. Cette chaine sera passée dans une fonction `string_to_array()` la transformant en `array` avec comme séparateur des `,` pour séparer les éléments de la chaine de texte vers des éléments de liste, caractère passé en second paramètre de la fonction. Cet `array` sera ensuite envoyé dans la fonction `jsonb_object()`. @@ -256,7 +256,7 @@ Vous pouvez voir que j'utilise l'opérateur `?` (uniquement vable pour les champ Je précise, même si si vous êtes encore ici je suppose que vous savez déjà cela, mais la forme `(quelque_chose)::int4` est un raccourci de PostgreSQL pour faire un `cast` soit convertir une valeur dans un autre type. Avec `->>` la valeur nous est renvoyée sour forme de texte et nous la convertissons en entier. -#### Avec du json imbriqué +### Avec du json imbriqué Bon, il est bien gentil, mais là son json reste du json où tout est au premier niveau. Bah oui, pour mon besoin cela m'a suffit. Mais je ne vais pas fuir mes responsabilités et on va voir comment cela se passe avec du json plus complexe. Je repartirai de l'exemple de la partie précedente pour compléxifier après cet apparté. @@ -287,10 +287,10 @@ SELECT FROM test.test ``` -Le `$` désigne le début du chemin json retourné. Nous faisons suivre ce premier symbole par un point pour passer à l'objet suivant puis par le nom de clé suivante et ainsi de suite juqu'à la clé recherchée, a laquelle nous collons un [1] pour la 2ème valeur de la liste (les valeurs commencent à 0). -Pour plus di'nformations sur les `json_path`, vous pouvez consulter la [documentation](https://www.postgresql.org/docs/current/functions-json.html#FUNCTIONS-SQLJSON-PATH) +Le `$` désigne le début du chemin json retourné. Nous faisons suivre ce premier symbole par un point pour passer à l'objet suivant puis par le nom de clé suivante et ainsi de suite juqu'à la clé recherchée, a laquelle nous collons un `[1]` pour la 2ème valeur de la liste (les valeurs commencent à 0). +Pour plus d'informations sur les `json_path`, vous pouvez consulter la [documentation](https://www.postgresql.org/docs/current/functions-json.html#FUNCTIONS-SQLJSON-PATH) -### Attaquons nous au recensement +## Attaquons nous au recensement Bien, maintenant que nous avons essayé d'expliquer tant bien que mal les concepts avec des exemples simples car il faut bien commencer quelque part, essayons avec quelque chose d'un peu plus volumineux. @@ -461,7 +461,7 @@ returning *; `-` est un opérateur qui permet de supprimer une clé d'un objet json. Pour notre UPDATE on enlève donc notre faute de frappe de l'ensemble de l'objet et on concatène tout le reste avec la construction d'un nouvel objet où on corrige le nom de la clé, et à laquelle on assigne la valeur de la clé en train d'être supprimée (elle est toujours utilisable au moment de l'UPDATE), sur les champs qui la contiennent à l'origine avec `?`. -### Et maintenant ? Qu'est-ce qu'on fait de ça ? +## Et maintenant ? Qu'est-ce qu'on fait de ça ? Jusqu'à maintenant, nous n'avons travaillé qu'avec le volet population pour son dernier millésime. Imaginons maintenant que nous répétions l'exercice pour les 6 volets et sur plusieurs millésimes et ce sachant qu'au cours du temps, certains champs peuvent apparaitre / disparaitre (changement dans les niveaux de diplômes observés par exemple). Il serait interessant de récupérer une table indiquant la première et la dernière année de présence de chacune des clés. Mettons que lors de ces travaux nous en avons profité pour mettre à jour une table "correspondance_clefs_champs" listant chacune des clés présente et son nom INSEE d'origine (tout du moins, celui que nous avions normalisé). @@ -532,7 +532,7 @@ WITH SELECT * FROM final WHERE valeur IS NOT NULL; ``` -![vm_presence_cles_annees](https://cdn.geotribu.fr/img/articles-blog-rdp/articles/2024/postgresql_json/vm_donnees_olap.png)vm_donnees_olap.png){: .img-center loading=lazy } +![vm_presence_cles_annees](https://cdn.geotribu.fr/img/articles-blog-rdp/articles/2024/postgresql_json/vm_donnees_olap.png){: .img-center loading=lazy } Attention, la création de cette vue matérialisée ou son refresh peut prendre un certain temps si vous avez stocké beaucoup de données (1 heure chez moi pour les 6 volets de 2015 à 2021). From a8dadea4bb0db8195831dabfb9c00c29eb98b212 Mon Sep 17 00:00:00 2001 From: Thomas Szczurek-Gayant Date: Wed, 1 Jan 2025 17:26:12 +0100 Subject: [PATCH 015/101] =?UTF-8?q?JSON+PG:=20d=C3=A9placement=20dans=2020?= =?UTF-8?q?25?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../2025-01-21_travailler-avec-JSON-et-PostgreSQL.md | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename content/articles/{2024 => 2025}/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md (100%) diff --git a/content/articles/2024/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md similarity index 100% rename from content/articles/2024/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md rename to content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md From e4a1d134efef8049c521ced514e2c1b3b32d5cad Mon Sep 17 00:00:00 2001 From: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> Date: Tue, 14 Jan 2025 19:10:36 +0100 Subject: [PATCH 016/101] Update content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md Co-authored-by: Julien Signed-off-by: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> --- .../2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md index 60d30b38e8..52073f989c 100644 --- a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md +++ b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md @@ -19,6 +19,8 @@ tags: # Travailler avec du JSON dans PostgreSQL +:calendar: Date de publication initiale : {{ page.meta.date | date_localized }} + Dans le cadre d'un projet personnel encadré de type "tu l'as fait pour les iris de Lille avec Sqlite, t'as vu que tu sais faire. Bon bah maintenant tu le fait pour toutes les communes de France dans PG", j'ai voulu stocker une bonne partie des données du recensement de l'Insee dans une base PostgreSQL avec des tables multimillésimes. Problème, même au sein d'un même jeu de données, les champs peuvent changer au cours des années et celà empêche de pouvoir dégager une structure de table fixe, ce qui est assez génant vous en conviendrez. La solution ? Passer par des données semi-structurées, soit stocker ces données en JSON dans le champ d'une table. Cet article se veut un condensé de cette expérience. !!! warning From 713a05fef1d4ee77bb81b069f396e2c97ce3e46c Mon Sep 17 00:00:00 2001 From: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> Date: Tue, 14 Jan 2025 19:13:42 +0100 Subject: [PATCH 017/101] Update content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md Co-authored-by: Julien Signed-off-by: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> --- .../2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md index 52073f989c..c68ed35253 100644 --- a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md +++ b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md @@ -14,7 +14,7 @@ license: default robots: index, follow tags: - PostgreSQL - - json + - JSON --- # Travailler avec du JSON dans PostgreSQL From 83a3def6f634a8c0b21b435ac87543a9ce0bc8e4 Mon Sep 17 00:00:00 2001 From: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> Date: Tue, 14 Jan 2025 19:14:28 +0100 Subject: [PATCH 018/101] Update content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Loïc Bartoletti Signed-off-by: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> --- .../2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md index c68ed35253..56a6411d57 100644 --- a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md +++ b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md @@ -28,7 +28,7 @@ Dans le cadre d'un projet personnel encadré de type "tu l'as fait pour les iris Puisque nous allons parler de json et de données semi-structurées, je me sens dans l'obligation de commencer cet article par un avertissement. -**Le modèle relationnel c'est bon, mangez en, et les contraintes d'intégrités ont été inventées pour de bonnes raisons.** +**Le modèle relationnel, c'est bon, mangez-en, et les contraintes d'intégrités ont été inventées pour de bonnes raisons.** Cet article ne se veut surtout pas être une invitation à partir en mode yolo sur la gestion des données "c'est bon ya qu'a tout mettre en json" (comme un vulgaire dev qui mettrait tout dans Mongodb diraient les mauvaises langues). From d416076daafd1879f311304d4b6def8e09471cb2 Mon Sep 17 00:00:00 2001 From: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> Date: Tue, 14 Jan 2025 19:15:14 +0100 Subject: [PATCH 019/101] Update content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Loïc Bartoletti Signed-off-by: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> --- .../2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md index 56a6411d57..74c3f82cc1 100644 --- a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md +++ b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md @@ -46,7 +46,7 @@ Pour celles et ceux qui ne connaisent pas le `json`, il s'agit d'un format textu {"nb_champignons": 42, "nb_tomates": 31, "prenom": "roger"} ``` -Les valeurs peuvent prendre deux formes. Soit une valeur unique comme dans l'exemple ci-dessus, soit un `array` (une liste) qu'on place entre `[]`, les deux pouvant êtres combinés au sein d'un seul objet json. +Les valeurs peuvent prendre deux formes. Soit une valeur unique comme dans l'exemple ci-dessus, soit un `array`, une liste, qu'on place entre `[]`, les deux pouvant êtres combinés au sein d'un seul objet json. ```json {"prenoms": ["elodie", "roger", "fatima"], "nb_champgnons": 42} From d79aafc445ffd9227e13a184a3e275fc5a323c49 Mon Sep 17 00:00:00 2001 From: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> Date: Tue, 14 Jan 2025 19:15:32 +0100 Subject: [PATCH 020/101] Update content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Loïc Bartoletti Signed-off-by: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> --- .../2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md index 74c3f82cc1..bb89f16efa 100644 --- a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md +++ b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md @@ -40,7 +40,7 @@ Pour celles et ceux qui ne connaisent pas le `json`, il s'agit d'un format textu {"clé_1": "valeur", "clé_2": "valeur", "clé_3": "valeur"} ``` -(pas besoin de guillemets pour les nombres) +pas besoin de guillemets pour les nombres ```json {"nb_champignons": 42, "nb_tomates": 31, "prenom": "roger"} From 5238f45720038c54920aa9495097a9791bbbcccd Mon Sep 17 00:00:00 2001 From: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> Date: Tue, 14 Jan 2025 19:16:23 +0100 Subject: [PATCH 021/101] Update content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Loïc Bartoletti Signed-off-by: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> --- .../2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md index bb89f16efa..e0f97d35a9 100644 --- a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md +++ b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md @@ -461,7 +461,7 @@ WHERE champ ? 'nme' returning *; ``` -`-` est un opérateur qui permet de supprimer une clé d'un objet json. Pour notre UPDATE on enlève donc notre faute de frappe de l'ensemble de l'objet et on concatène tout le reste avec la construction d'un nouvel objet où on corrige le nom de la clé, et à laquelle on assigne la valeur de la clé en train d'être supprimée (elle est toujours utilisable au moment de l'UPDATE), sur les champs qui la contiennent à l'origine avec `?`. +`-` est un opérateur qui permet de supprimer une clé d'un objet json. Pour notre UPDATE on enlève donc notre faute de frappe de l'ensemble de l'objet. De plus, on concatène tout le reste avec la construction d'un nouvel objet où l'on corrige le nom de la clé. On assigne également la valeur de la clé en train d'être supprimée, elle est toujours utilisable au moment de l'UPDATE, sur les champs qui la contiennent à l'origine avec `?`. ## Et maintenant ? Qu'est-ce qu'on fait de ça ? From 4666678728b02d0eef061a2981652f12ca03cdcd Mon Sep 17 00:00:00 2001 From: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> Date: Tue, 14 Jan 2025 19:18:48 +0100 Subject: [PATCH 022/101] Update content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Loïc Bartoletti Signed-off-by: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> --- .../2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md index e0f97d35a9..6b62df9b1b 100644 --- a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md +++ b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md @@ -465,7 +465,7 @@ returning *; ## Et maintenant ? Qu'est-ce qu'on fait de ça ? -Jusqu'à maintenant, nous n'avons travaillé qu'avec le volet population pour son dernier millésime. Imaginons maintenant que nous répétions l'exercice pour les 6 volets et sur plusieurs millésimes et ce sachant qu'au cours du temps, certains champs peuvent apparaitre / disparaitre (changement dans les niveaux de diplômes observés par exemple). Il serait interessant de récupérer une table indiquant la première et la dernière année de présence de chacune des clés. Mettons que lors de ces travaux nous en avons profité pour mettre à jour une table "correspondance_clefs_champs" listant chacune des clés présente et son nom INSEE d'origine (tout du moins, celui que nous avions normalisé). +Jusqu'à maintenant, nous n'avons travaillé qu'avec le volet population pour son dernier millésime. Imaginons maintenant que nous répétions l'exercice pour les 6 volets et sur plusieurs millésimes, et ce, en sachant qu'au cours du temps, certains champs peuvent apparaitre ou disparaitre ; changement dans les niveaux de diplômes observés par exemple. Il serait intéressant de récupérer une table indiquant la première et la dernière année de présence de chaque clé. Mettons que lors de ces travaux, nous en avons profité pour mettre à jour une table "correspondance_clefs_champs" listant chaque clé présentes et son nom INSEE d'origine (tout du moins, celui que nous avions normalisé). ```sql CREATE MATERIALIZED VIEW insee.presence_clefs_annees AS From 95ac377335bd2dc74421001ec66a00267f2fccd4 Mon Sep 17 00:00:00 2001 From: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> Date: Tue, 14 Jan 2025 19:21:16 +0100 Subject: [PATCH 023/101] Update content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Loïc Bartoletti Signed-off-by: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> --- .../2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md index 6b62df9b1b..ae6c3fb54b 100644 --- a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md +++ b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md @@ -539,7 +539,7 @@ SELECT * FROM final WHERE valeur IS NOT NULL; Attention, la création de cette vue matérialisée ou son refresh peut prendre un certain temps si vous avez stocké beaucoup de données (1 heure chez moi pour les 6 volets de 2015 à 2021). Enfin, histoire de vivre avec son temps et non comme un viel ours des cavernes ou comme un data machin de collectivité territoriale (j'y bosse, j'ai le droit), on va convertir cette vue matérialisée en fichier [parquet](https://parquet.apache.org/). -Et pour ça on va utiliser gdal/ogr qui est décidemment incroyable. +Et, pour ça, on va utiliser gdal/ogr qui est décidément incroyable. ```sh ogr2ogr -of parquet donnees_insee.parquet PG:"dbname='insee' shcema='insee' tables='donnees_communes_olap' user='nom_utilisateur' password='votre_mot_de_passe'" From 1fe57c64b792c7e5f4f6229f3dc417806b9e0b6a Mon Sep 17 00:00:00 2001 From: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> Date: Tue, 14 Jan 2025 19:36:02 +0100 Subject: [PATCH 024/101] Update content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Loïc Bartoletti Signed-off-by: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> --- .../2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md index ae6c3fb54b..7bf29f7891 100644 --- a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md +++ b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md @@ -104,7 +104,7 @@ Exemple issu du site de [Mozilla](https://developer.mozilla.org/fr/docs/Learn/Ja PostgreSQL est capable de stocker les données/objets au format json dans des champs auxquels on attribuera un type dédié. Il y en a deux car sinon ça ne serait pas rigolo. -- Le type `json` qui est là pour des raisons historiques et laisser fonctionner des bases qui auraient utilisé ce type dans le passé. Il stocke les informations sous forme textuelle ce qui peu optimisé pour un ordinateur. Il existe toutefois un intérêt à l'utiliser : il permet de retrouver l'information sur l'ordre des clés. Si il est important pour vous de savoir que `nom` est la clef 1 et `prenom` la clef 2, sans avoir à repasser par le nom de la clef, alors il vous faudra passer par le type `json`. +- Le type `json` qui est là pour des raisons historiques et laisser fonctionner des bases qui auraient utilisé ce type dans le passé. Il stocke les informations sous forme textuelle, ce qui est peu optimisé pour un ordinateur. Il existe toutefois un intérêt à l'utiliser : il permet de retrouver l'information sur l'ordre des clés. S'il est important pour vous de savoir que `nom` est la clef 1 et `prenom` la clef 2, sans avoir à repasser par le nom de la clef, alors il vous faudra passer par le type `json`. - le type `jsonb`. Le type moderne. Il stocke les informations sous forme binaire et énormément de fonctions sont disponibles en plus de celles du type `json`. From 84e98ebd0f360df0851a908452450fb16e64fba4 Mon Sep 17 00:00:00 2001 From: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> Date: Tue, 14 Jan 2025 19:36:22 +0100 Subject: [PATCH 025/101] Update content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Loïc Bartoletti Signed-off-by: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> --- .../2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md index 7bf29f7891..cd23f3afc6 100644 --- a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md +++ b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md @@ -122,7 +122,7 @@ Pour indexer des valeurs plus "profondes", il faudra passer par des indexs fonct CREATE INDEX idx_tb_champjson ON tb USING gin (champ_json -> cle_ou_se_situent_les_valeur_a_indexer); ``` -(on va expliquer ce `->` par la suite) +On expliquera ce `->` par la suite. Pour les aventuriers et aventurières, il existe une extention de PostgreSQL `btree_gin` permettant de faire des index multi-champs mixant des champs classiques et json. Elle est disponible nativement à l'installation de PostgreSQL et ne vous demandera pas de devenir développeur.se C/C++ pour l'installer (coucou les fdw parquet ! Ca va par chez vous ?). From 6eb3d40a1b88140976d376ef667ec024bf8b7815 Mon Sep 17 00:00:00 2001 From: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> Date: Tue, 14 Jan 2025 19:36:47 +0100 Subject: [PATCH 026/101] Update content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Loïc Bartoletti Signed-off-by: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> --- .../2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md index cd23f3afc6..eebe721f1f 100644 --- a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md +++ b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md @@ -110,7 +110,7 @@ PostgreSQL est capable de stocker les données/objets au format json dans des ch ## Les index -Il est possible d'indexer un champ de type `json` / `jsonb` sur ses clés **de premier niveau** et ça se fait avec des index de type `GIN`. +Il est possible d'indexer un champ de type `json` / `jsonb` sur ses clés **de premier niveau** et cela se fait avec des index de type `GIN` : ```sql CREATE INDEX idx_tb_champjson ON tb USING gin (champ_json); From 7d7c95845925ad2724ad6a0b67ebdcff53318bdd Mon Sep 17 00:00:00 2001 From: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> Date: Tue, 14 Jan 2025 19:37:01 +0100 Subject: [PATCH 027/101] Update content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Loïc Bartoletti Signed-off-by: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> --- .../2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md index eebe721f1f..cb39612a41 100644 --- a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md +++ b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md @@ -116,7 +116,7 @@ Il est possible d'indexer un champ de type `json` / `jsonb` sur ses clés **de p CREATE INDEX idx_tb_champjson ON tb USING gin (champ_json); ``` -Pour indexer des valeurs plus "profondes", il faudra passer par des indexs fonctionnels (index sur des fonctions) +Pour indexer des valeurs plus "profondes", il faudra passer par des indexs fonctionnels, index sur des fonctions : ```sql CREATE INDEX idx_tb_champjson ON tb USING gin (champ_json -> cle_ou_se_situent_les_valeur_a_indexer); From 995e5128214b663df6dab6fdf0b51890dc104308 Mon Sep 17 00:00:00 2001 From: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> Date: Tue, 14 Jan 2025 19:40:46 +0100 Subject: [PATCH 028/101] Update content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Loïc Bartoletti Signed-off-by: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> --- .../2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md index cb39612a41..d1d02390f5 100644 --- a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md +++ b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md @@ -130,7 +130,7 @@ Pour les aventuriers et aventurières, il existe une extention de PostgreSQL `bt Je vais éviter de vous spammer du DDL, mais vous pourrez retrouver le schéma complet de la base [ici](https://github.com/thomas-szczurek/base_donnees_insee/tree/main/sql/creation_tables) -Partant d'un schéma nommé `insee`, on va créer deux tables. La première contiendra la liste des *bases* disponibles (les différentes volets du recensement) et une seconde permettant de stocker les données (pour rester concentré sur le json, on va s'épargner 95% du modèle sous jacent, on ne gérera donc par ici les codes communes etc...). En bonus pour celles et ceux voulant tester ce que Postgres a dans le ventre, je proposerai une variante juste en dessous : +Partant d'un schéma nommé `insee`, on va créer deux tables. La première contiendra la liste des *bases* disponibles, les différents volets du recensement. Une seconde permettant de stocker les données ; pour rester concentré sur le json, on va s'épargner 95% du modèle sous-jacent. On ne gérera donc par ici les codes communes, etc. En bonus pour celles et ceux voulant tester ce que Postgres a dans le ventre, je proposerai une variante juste en dessous : ```sql BEGIN; From 4b87426e828aeed6767a66d075f19c11507a074e Mon Sep 17 00:00:00 2001 From: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> Date: Tue, 14 Jan 2025 19:41:09 +0100 Subject: [PATCH 029/101] Update content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Loïc Bartoletti Signed-off-by: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> --- .../2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md index d1d02390f5..9e8faa299a 100644 --- a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md +++ b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md @@ -134,7 +134,7 @@ Partant d'un schéma nommé `insee`, on va créer deux tables. La première cont ```sql BEGIN; --- table listant les differentes bases insee disponibles +-- table listant les differentes bases Insee disponibles CREATE TABLE insee.bases ( pk_id int2 PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, From ca2c8c35a0c03c10a624ee6920be8fb6c1760256 Mon Sep 17 00:00:00 2001 From: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> Date: Tue, 14 Jan 2025 19:41:48 +0100 Subject: [PATCH 030/101] Update content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Loïc Bartoletti Signed-off-by: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> --- .../2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md index 9e8faa299a..e85d45bcaf 100644 --- a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md +++ b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md @@ -159,7 +159,7 @@ CREATE INDEX idx_donnees_com_donnees ON insee.donnees_communes USING gin (donnee END; ``` -Les plus tatillons des dba d'entre vous auront remarqués ce `CHECK`. "Mais pourquoi qu'il utilise pas varchar(5), il fait vraiment n'importe quoi". Tout simplement car cette forme permet d'utiliser un type de texte aux nombre de caractères **réellement** arbitraire (le type `text`) contrairement à varchar(255), tout en pouvant en contrôler le nombre minimum et maximum avec le prédicat `CHECK` (contrairement à varchar qui ne contrôle que le maximum) comme expliqué sur le [wiki Postgres](https://wiki.postgresql.org/wiki/Don%27t_Do_This#Don.27t_use_varchar.28n.29_by_default). +Les plus tatillons des DBA d'entre vous auront remarqué ce `CHECK`. "Mais pourquoi qu'il utilise pas varchar(5) ? Il fait vraiment n'importe quoi !" Tout simplement, car cette forme permet d'utiliser un type de texte aux nombres de caractère **réellement** arbitraire (le type `text`) contrairement à varchar(255), tout en pouvant en contrôler le nombre minimum et maximum avec le prédicat `CHECK` (contrairement à varchar qui ne contrôle que le maximum) comme expliqué sur le [wiki Postgres](https://wiki.postgresql.org/wiki/Don%27t_Do_This#Don.27t_use_varchar.28n.29_by_default). Et on insère quleques ligne dans notre table insee.bases From d6d9836a3f0fab820a44b04ed6a2c59dddc85ceb Mon Sep 17 00:00:00 2001 From: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> Date: Tue, 14 Jan 2025 19:42:09 +0100 Subject: [PATCH 031/101] Update content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Loïc Bartoletti Signed-off-by: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> --- .../2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md index e85d45bcaf..297757498d 100644 --- a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md +++ b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md @@ -161,7 +161,7 @@ END; Les plus tatillons des DBA d'entre vous auront remarqué ce `CHECK`. "Mais pourquoi qu'il utilise pas varchar(5) ? Il fait vraiment n'importe quoi !" Tout simplement, car cette forme permet d'utiliser un type de texte aux nombres de caractère **réellement** arbitraire (le type `text`) contrairement à varchar(255), tout en pouvant en contrôler le nombre minimum et maximum avec le prédicat `CHECK` (contrairement à varchar qui ne contrôle que le maximum) comme expliqué sur le [wiki Postgres](https://wiki.postgresql.org/wiki/Don%27t_Do_This#Don.27t_use_varchar.28n.29_by_default). -Et on insère quleques ligne dans notre table insee.bases +Et on insère quelques lignes dans notre table insee.bases ```sql INSERT INTO insee.bases (nom) VALUES From 7441800a3d26a02f5e004c699f3a2f7dcd014683 Mon Sep 17 00:00:00 2001 From: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> Date: Tue, 14 Jan 2025 19:42:25 +0100 Subject: [PATCH 032/101] Update content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Loïc Bartoletti Signed-off-by: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> --- .../2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md index 297757498d..beade7986a 100644 --- a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md +++ b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md @@ -175,7 +175,7 @@ INSERT INTO insee.bases (nom) VALUES ### (Bonus) Partition de la table données -Imaginons que nous travaillons sur l'ensemble des données du recensement (soit 6 fichiers sources), de 2015 à 2021 pour environ 35 000 communes. On va arriver sur du 1,5 millions d'enregistrements. Entendons nous bien, à l'échelle de Postgres ça ne reste pas grand chose. Mais, si comme moi vous voulez voir ce qu'on peut tirer des entrailles de Postres, ça permet de pouvoir commencer à justifier d'utiliser certaines fonctionnalités avancées. Une table partitionnée c'est quoi ? +Imaginons que nous travaillons sur l'ensemble des données du recensement (soit 6 fichiers sources), de 2015 à 2021 pour environ 35 000 communes. On va arriver sur du 1,5 million d'enregistrements. Entendons-nous bien, à l'échelle de Postgres ça ne reste pas grand-chose. Mais, si comme moi, vous voulez voir ce qu'on peut tirer des entrailles de Postres, ça permet de pouvoir commencer à justifier d'utiliser certaines fonctionnalités avancées. Une table partitionnée, c'est quoi ? - C'est une table découpée en plusieurs morceaux où une table *parent* contrôlera les tables *enfants*. - Une table partitionée peut se découper selon la valeur d'un champ ou une période temporelle ("fait moi une partition tous les mois") From 84a3799fefac5e07ca62c2913cb67f9db7d27096 Mon Sep 17 00:00:00 2001 From: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> Date: Tue, 14 Jan 2025 19:42:47 +0100 Subject: [PATCH 033/101] Update content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Loïc Bartoletti Signed-off-by: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> --- .../2025-01-21_travailler-avec-JSON-et-PostgreSQL.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md index beade7986a..c739b65b73 100644 --- a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md +++ b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md @@ -178,14 +178,14 @@ INSERT INTO insee.bases (nom) VALUES Imaginons que nous travaillons sur l'ensemble des données du recensement (soit 6 fichiers sources), de 2015 à 2021 pour environ 35 000 communes. On va arriver sur du 1,5 million d'enregistrements. Entendons-nous bien, à l'échelle de Postgres ça ne reste pas grand-chose. Mais, si comme moi, vous voulez voir ce qu'on peut tirer des entrailles de Postres, ça permet de pouvoir commencer à justifier d'utiliser certaines fonctionnalités avancées. Une table partitionnée, c'est quoi ? - C'est une table découpée en plusieurs morceaux où une table *parent* contrôlera les tables *enfants*. -- Une table partitionée peut se découper selon la valeur d'un champ ou une période temporelle ("fait moi une partition tous les mois") +- Une table partitionnée peut se découper selon la valeur d'un champ ou une période temporelle ("fais -moi une partition tous les mois") - On peut requêter la table parent ou les tables enfants. -- Une table doit être partitionnée à sa création, une table déjà existante ne peut pas être convertie. De nouvelles partitions peuvent toutefois êtres créées à volonté. -- Celà peut être interessant pour limiter la taille des scans séquentiels qui se feront sur des tables plus petites que la table *parent*, où pour se faciliter la gestion de tables volumineuses. -- On peut attacher / détacher une partition (qui devient alors une table classique) avec les "mots" `ATTACH PARTITION` / `DETACH PARTITION` y comprit sur une base en production grace à l'option `CONCURENTLY` -- Une clé primaire de table partitionnée **doit** être composite, c'est à dire que son unicité sera vérifiée par la composition de plusieurs champs, et contenir obligatoirement le champ de partitionnement. +- Une table doit être partitionnée à sa création, une table déjà existante ne peut pas être convertie. De nouvelles partitions peuvent toutefois être créées à volonté. +- Cela peut être intéressant pour limiter la taille des scans séquentiels qui se feront sur des tables plus petites que la table *parent*, où pour se faciliter la gestion de tables volumineuses. +- On peut attacher / détacher une partition (qui devient alors une table classique) avec les "mots" `ATTACH PARTITION` / `DETACH PARTITION` y compris sur une base en production grâce à l'option `CONCURENTLY` +- Une clé primaire de table partitionnée **doit** être composite, c'est-à-dire que son unicité sera vérifiée par la composition de plusieurs champs et contenir obligatoirement le champ de partitionnement. -(vous aussi lire le contenu de la [documentation à se sujet](https://doc.postgresql.fr/17/ddl-partitioning.html)) +Vous pouvez aussi lire le contenu de la [documentation à ce sujet](https://doc.postgresql.fr/17/ddl-partitioning.html) A quoi ressemblerai la création de insee.donnees_communes si on la partitionnait selon les différentes sources de données ? From 944d6698ac69948b0b815dfc958b530235885c03 Mon Sep 17 00:00:00 2001 From: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> Date: Tue, 14 Jan 2025 19:43:13 +0100 Subject: [PATCH 034/101] Update content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Loïc Bartoletti Signed-off-by: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> --- .../2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md index c739b65b73..d84b2d5497 100644 --- a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md +++ b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md @@ -187,7 +187,7 @@ Imaginons que nous travaillons sur l'ensemble des données du recensement (soit Vous pouvez aussi lire le contenu de la [documentation à ce sujet](https://doc.postgresql.fr/17/ddl-partitioning.html) -A quoi ressemblerai la création de insee.donnees_communes si on la partitionnait selon les différentes sources de données ? +À quoi ressemblerait la création de insee.donnees_communes si on la partitionnait selon les différentes sources de données ? ```sql BEGIN; From 1c6e87719ca4ac02f94208da774e40a2ad2bb5ad Mon Sep 17 00:00:00 2001 From: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> Date: Tue, 14 Jan 2025 19:46:06 +0100 Subject: [PATCH 035/101] Update content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Loïc Bartoletti Signed-off-by: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> --- .../2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md index d84b2d5497..dbff90e87a 100644 --- a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md +++ b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md @@ -214,7 +214,7 @@ On remarque quelques changements : la déclaration de la clé primaire composite ## Insertion de données et récupération -Pour passer des données textuelles / sql vers des données encodées en json dans le champ dédié qui va bien, PostgreSQL dispose de [*quelques* fonctions](https://docs.postgresql.fr/17/functions-json.html#FUNCTIONS-JSON-PROCESSING). Nous allons utiliser la fonction `jsonb_object()` qui permet de transformer un `array` sql sous forme `clef1, valeur1, clef2, valeur2 ....` en objet `jsonb` qui n'aura qu'un niveau d'imbrication. D'autres fonctions sont disponibles pour des objets plus complexes (comme `jsonb_build_object()`). +Pour passer des données textuelles / SQL vers des données encodées en JSON dans le champ dédié qui va bien, PostgreSQL dispose de [*quelques* fonctions](https://docs.postgresql.fr/17/functions-json.html#FUNCTIONS-JSON-PROCESSING). Nous allons utiliser la fonction `jsonb_object()` qui permet de transformer un `array` sql sous forme `clef1, valeur1, clef2, valeur2 ....` en objet `jsonb` qui n'aura qu'un niveau d'imbrication. D'autres fonctions sont disponibles pour des objets plus complexes (comme `jsonb_build_object()`). ### Exemple simple From 356f316db77070f93a6b8aebf73bb3be05c5b59c Mon Sep 17 00:00:00 2001 From: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> Date: Tue, 14 Jan 2025 19:46:21 +0100 Subject: [PATCH 036/101] Update content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Loïc Bartoletti Signed-off-by: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> --- .../2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md index dbff90e87a..49de80cb2c 100644 --- a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md +++ b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md @@ -218,7 +218,7 @@ Pour passer des données textuelles / SQL vers des données encodées en JSON da ### Exemple simple -On va créer une chaine de texte qui contiendra le contenu de notre json dont les valeurs serons séparées par des virgules `clé1,valeur1, clé2, valeur2`. Cette chaine sera passée dans une fonction `string_to_array()` la transformant en `array` avec comme séparateur des `,` pour séparer les éléments de la chaine de texte vers des éléments de liste, caractère passé en second paramètre de la fonction. Cet `array` sera ensuite envoyé dans la fonction `jsonb_object()`. +On va créer une chaine de texte qui contiendra le contenu de notre json dont les valeurs seront séparées par des virgules `clé1,valeur1, clé2, valeur2`. Cette chaine sera passée dans une fonction `string_to_array()` la transformant en `array` avec comme séparateur des `,` pour séparer les éléments de la chaine de texte vers des éléments de liste, caractère passé en second paramètre de la fonction. Cet `array` sera ensuite envoyé dans la fonction `jsonb_object()`. ```sql INSERT INTO insee.donnees_communes (code_commune, annee, fk_base, donnees) VALUES From 7342c2a2c03652ea4fc909f31e017eb944ecdbd6 Mon Sep 17 00:00:00 2001 From: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> Date: Tue, 14 Jan 2025 19:47:12 +0100 Subject: [PATCH 037/101] Update content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Loïc Bartoletti Signed-off-by: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> --- .../2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md index 49de80cb2c..d68892cd54 100644 --- a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md +++ b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md @@ -254,7 +254,7 @@ WHERE donnees ? 'melons' ``` -Vous pouvez voir que j'utilise l'opérateur `?` (uniquement vable pour les champs `jsonb` et non ceux en simple `json`). En effet, lorsque l'on requête un champ json/jsonb, un retour vous est fait pour l'ensemble les enregistrements de la table, même ceux ne contenant pas la clé. Comprendre que si votre table comprends 100 000 enregistrements, mais que seuls 100 contiennent la clé "melons", ne pas spécifier cette clause WHERE vous renverait 100 000 lignes, dont 99 900 de `NULL`.`?` est un opérateur json permettant de poser la question "la clé est-elle présente au premier niveau du champ json pour cet enregistrement ?", et on ne récupérerait que nos 100 enregistrements contenant la clé "melons". +Vous pouvez voir que j'utilise l'opérateur `?`; uniquement valable pour les champs `jsonb` et non ceux en simple `json`. En effet, lorsque l'on requête un champ json/jsonb, un retour vous est fait pour l'ensemble des enregistrements de la table, même ceux ne contenant pas la clé. Comprendre que si votre table comprend 100 000 enregistrements, mais que seuls 100 contiennent la clé "melons", ne pas spécifier cette clause WHERE vous renverrait 100 000 lignes, dont 99 900 de `NULL`.`?` est un opérateur json permettant de poser la question "la clé est-elle présente au premier niveau du champ json pour cet enregistrement ?", et on ne récupérerait que nos 100 enregistrements contenant la clé "melons". Je précise, même si si vous êtes encore ici je suppose que vous savez déjà cela, mais la forme `(quelque_chose)::int4` est un raccourci de PostgreSQL pour faire un `cast` soit convertir une valeur dans un autre type. Avec `->>` la valeur nous est renvoyée sour forme de texte et nous la convertissons en entier. From 6965f140e1ec7fef3897896f40bb48ee773355fc Mon Sep 17 00:00:00 2001 From: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> Date: Tue, 14 Jan 2025 19:47:40 +0100 Subject: [PATCH 038/101] Update content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Loïc Bartoletti Signed-off-by: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> --- .../2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md index d68892cd54..db12061533 100644 --- a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md +++ b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md @@ -256,7 +256,7 @@ WHERE Vous pouvez voir que j'utilise l'opérateur `?`; uniquement valable pour les champs `jsonb` et non ceux en simple `json`. En effet, lorsque l'on requête un champ json/jsonb, un retour vous est fait pour l'ensemble des enregistrements de la table, même ceux ne contenant pas la clé. Comprendre que si votre table comprend 100 000 enregistrements, mais que seuls 100 contiennent la clé "melons", ne pas spécifier cette clause WHERE vous renverrait 100 000 lignes, dont 99 900 de `NULL`.`?` est un opérateur json permettant de poser la question "la clé est-elle présente au premier niveau du champ json pour cet enregistrement ?", et on ne récupérerait que nos 100 enregistrements contenant la clé "melons". -Je précise, même si si vous êtes encore ici je suppose que vous savez déjà cela, mais la forme `(quelque_chose)::int4` est un raccourci de PostgreSQL pour faire un `cast` soit convertir une valeur dans un autre type. Avec `->>` la valeur nous est renvoyée sour forme de texte et nous la convertissons en entier. +Je précise que même si vous êtes encore ici, je suppose que vous savez déjà cela. Cependant, la forme `(quelque_chose)::int4` est un raccourci de PostgreSQL pour faire un `cast` soit convertir une valeur dans un autre type. Avec `->>` la valeur nous est renvoyée sous forme de texte et nous la convertissons en entier. ### Avec du json imbriqué From 414bb2060aa72b2918255417547a270694f4d7dc Mon Sep 17 00:00:00 2001 From: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> Date: Tue, 14 Jan 2025 19:47:58 +0100 Subject: [PATCH 039/101] Update content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Loïc Bartoletti Signed-off-by: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> --- .../2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md index db12061533..ad8a3601e6 100644 --- a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md +++ b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md @@ -260,9 +260,9 @@ Je précise que même si vous êtes encore ici, je suppose que vous savez déjà ### Avec du json imbriqué -Bon, il est bien gentil, mais là son json reste du json où tout est au premier niveau. Bah oui, pour mon besoin cela m'a suffit. Mais je ne vais pas fuir mes responsabilités et on va voir comment cela se passe avec du json plus complexe. Je repartirai de l'exemple de la partie précedente pour compléxifier après cet apparté. +Bon, il est bien gentil, mais là son json reste du json où tout est au premier niveau. Bah oui, pour mon besoin, cela m'a suffi. Mais, je ne vais pas fuir mes responsabilités et on va voir comment cela se passe avec du json plus complexe. Je repartirai de l'exemple de la partie précédente pour complexifier après cet aparté. -Pour injecter du json complexe dans un champs, deux solutions s'offrent à nous : imbriquer les fonctions dédiées, ou caster une chaine de texte. Imaginons un table "test", dans un schema "test" et dont un champ `jsonb` se nomme "donnees". +Pour injecter du json complexe dans un champ, deux solutions s'offrent à nous : imbriquer les fonctions dédiées, ou caster une chaine de texte. Imaginons une table "test", dans un schema "test" et dont un champ `jsonb` se nomme "donnees". ```sql INSERT INTO test.test (donnees) VALUES From e5d3b5805a26a9c6fac002aa29a65f34fdd95321 Mon Sep 17 00:00:00 2001 From: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> Date: Tue, 14 Jan 2025 19:48:15 +0100 Subject: [PATCH 040/101] Update content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Loïc Bartoletti Signed-off-by: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> --- .../2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md index ad8a3601e6..e00a9ee7b2 100644 --- a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md +++ b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md @@ -274,7 +274,7 @@ INSERT INTO test.test (donnees) VALUES ) ``` -Cette insertion pourrait tout aussi bien s'écrire avec un cast d'une chaine de texte vers du jsonb, attention, la syntaxe json doit être ici respectée : +Cette insertion pourrait tout aussi bien s'écrire avec un cast d'une chaine de texte vers du jsonb. Attention, la syntaxe json doit être ici respectée : ```sql INSERT INTO test.test (donnees) VALUES From 0fb68bec599e1e1b6afd672d6c973f5e3cd024a2 Mon Sep 17 00:00:00 2001 From: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> Date: Tue, 14 Jan 2025 19:48:58 +0100 Subject: [PATCH 041/101] Update content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Loïc Bartoletti Signed-off-by: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> --- ...025-01-21_travailler-avec-JSON-et-PostgreSQL.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md index e00a9ee7b2..846e516f0b 100644 --- a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md +++ b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md @@ -303,17 +303,17 @@ Les noms de champs commencent tous par P ou C, ceci indique *exploitation princi Vous trouverez [ici](https://github.com/thomas-szczurek/base_donnees_insee/blob/main/sql/import/correction_champs_insee.xlsx) un tableur dont le rôle est de s'occuper de tout ça. - `STXT` est la fonction qui permet de découper une chaine de texte avec Excel, avec comme arguments `cellule comprenant la chaine initiale` , `position du début de la découpe` , `position de fin de la découpe` -- A partir du fichier récupéré, il vous suffit de copier coller les noms de champs INSEE sur la première ligne et de récupérer la seconde, qui contiendra les noms de champs formatés. -- On remplace les noms de champs du fichier INSEE original. -- Et on change le nom de cette table temportaire par "rp_population_import.csv" +- À partir du fichier récupéré, il vous suffit de copier-coller les noms de champs INSEE sur la première ligne et de récupérer la seconde, qui contiendra les noms de champs formatés. +- On remplace les noms de champ du fichier INSEE original. +- Et on change le nom de cette table temporaire par "rp_population_import.csv" -Avant d'insérer les données dans notre table, nous allons passer par une table temporaire afin de rendre les données accessibles dans Postgres. Utiliser `COPY` de Postgresql serait fastidieux car il faudrait indiquer la centaine de champs que contient le volet population du recensement dans la commande. Et je n'ai pas honte de dire que j'ai un baobab dans la main à cette idée. Nous sortons donc ce merveilleux logiciel qu'est QGIS, on active les panneaux Explorateur et Explorateur2, on se créé une conection vers la base avec les droits de création, et d'un mouvement gracile du poignet vous glissez le fichier depuis le panneau Explorateur vers la base Postgres dans l'Explorateur2. Laisser la magie opérer. +Avant d'insérer les données dans notre table, nous allons passer par une table temporaire afin de rendre les données accessibles dans Postgres. Utiliser `COPY` de Postgresql serait fastidieux car il faudrait indiquer la centaine de champs que contient le volet population du recensement dans la commande. Et, je n'ai pas honte de dire que j'ai un baobab dans la main à cette idée. Nous sortons donc ce merveilleux logiciel qu'est QGIS. On active les panneaux Explorateur et Explorateur2. On crée une connexion vers la base avec les droits de création, et d'un mouvement gracile du poignet, vous glissez le fichier depuis le panneau Explorateur vers la base Postgres dans l'Explorateur2. Laisser la magie opérer. -Maintenant préparez vous pour peut-être un des INSERT les plus bizarre de votre vie (en tout ça la été pour moi !). Arf. Je me rend compte que si je veux bien faire il faut aussi que j'explique les CTE (c'est très étrange car encore une fois, si vous êtes encore ici vous savez très probablement déjà ce qu'est une CTE). +Maintenant, préparez-vous pour peut-être un des INSERT les plus bizarres de votre vie (en tout cas, ça l'a été pour moi !). Arf. Je me rends compte que si je veux bien faire, il faut aussi que j'explique les CTE (c'est très étrange, car encore une fois, si vous êtes encore ici, vous savez très probablement déjà ce qu'est une CTE). -CTE veut dire Common Table Expression. C'est une fonctionnalité qui permet, grace à la clause `WITH` d'isoler une sous requête de la requête principale pour rendre tout un peu plus clair, ou de la nommer pour pouvoir la réutiliser à plusieurs endroits sans devoir la réécrire. On peut aussi s'en servir pour faire des requêtes récursives avec `WITH RECURSIVE` et si le sujet vous interesse je vous encurange à aller lire la [magnifique documentation](https://www.postgresql.org/docs/current/queries-with.html#QUERIES-WITH-RECURSIVE) de Postgres a ce sujet. +CTE veut dire Common Table Expression. C'est une fonctionnalité qui permet grâce à la clause `WITH` d'isoler une sous-requête de la requête principale pour rendre tout un peu plus clair. Elle permet également de la nommer pour pouvoir la réutiliser à plusieurs endroits sans devoir la réécrire. On peut aussi s'en servir pour faire des requêtes récursives avec `WITH RECURSIVE` et si le sujet vous intéresse je vous encourage à aller lire la [magnifique documentation](https://www.postgresql.org/docs/current/queries-with.html#QUERIES-WITH-RECURSIVE) de Postgres a ce sujet. -On va utiliser la CTE pour concatenner le nom que l'on veut donner à nos clés avec les valeurs contenues dans notre table temporaire dans une chaine séparée par des `,`, qu'on enverra dans une fonction `string_to_array()` puis dans une fonction `jsonb_object()`. On en profiterra pour au passage nettoyer toute tabulation ou retour chariot qui pourrait subsister avec une expression régulière grace a la fonction `regex_replace()`. (ces caractères se notent `\t`, `\n` et `\r`). Cette dernière fonction prend 3 arguments : la chaine de caractère source, le `pattern` recherché, le texte de remplacement. On y ajoute le *drapeau* optionnel `g` afin de remplacer toutes les occurences trouvées. +On va utiliser la CTE pour concaténer le nom que l'on veut donner à nos clés avec les valeurs contenues dans notre table temporaire dans une chaine séparée par des `,`. On l'enverra dans une fonction `string_to_array()` puis dans une fonction `jsonb_object()`. On en profitera pour au passage, nettoyer toute tabulation ou retour chariot qui pourrait subsister avec une expression régulière grâce à la fonction `regex_replace()`. (ces caractères se notent `\t`, `\n` et `\r`). Cette dernière fonction prend 3 arguments : la chaine de caractères source, le `pattern` recherché, le texte de remplacement. On y ajoute le *drapeau* optionnel `g` afin de remplacer toutes les occurrences trouvées. Notez que si votre table temporaire possède un nom différent de "rp_population_import" il vous faudra modifier la clause FROM de la CTE. From 0aa459e3c4f6299e5004ec93f3edc3a0b849210b Mon Sep 17 00:00:00 2001 From: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> Date: Tue, 14 Jan 2025 19:49:24 +0100 Subject: [PATCH 042/101] Update content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Loïc Bartoletti Signed-off-by: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> --- .../2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md index 846e516f0b..8808c88620 100644 --- a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md +++ b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md @@ -281,7 +281,7 @@ INSERT INTO test.test (donnees) VALUES ('{"cle1": "valeur1", "cle2": ["foo", "bar", "baz"]}'::jsonb) ``` -Pour récupérer une valeur, on utilise la fonction `jsonb_path_query()` qui possède deux paramètres : `le nom du champ` contenant les données json, et le `json_path` vers la valeur a atteindre. Imaginons que nous voulions récupérer la deuxième valeur de la liste contenue dans "cle2" : +Pour récupérer une valeur, on utilise la fonction `jsonb_path_query()` qui possède deux paramètres : `le nom du champ` contenant les données json, et le `json_path` vers la valeur à atteindre. Imaginons que nous voulions récupérer la deuxième valeur de la liste contenue dans "cle2" : ```sql SELECT From 85904df6946b10434c51b187e23f12c3bb8cbad7 Mon Sep 17 00:00:00 2001 From: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> Date: Tue, 14 Jan 2025 19:50:22 +0100 Subject: [PATCH 043/101] Update content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Loïc Bartoletti Signed-off-by: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> --- .../2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md index 8808c88620..4db7725166 100644 --- a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md +++ b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md @@ -289,7 +289,7 @@ SELECT FROM test.test ``` -Le `$` désigne le début du chemin json retourné. Nous faisons suivre ce premier symbole par un point pour passer à l'objet suivant puis par le nom de clé suivante et ainsi de suite juqu'à la clé recherchée, a laquelle nous collons un `[1]` pour la 2ème valeur de la liste (les valeurs commencent à 0). +Le `$` désigne le début du chemin json retourné. Nous faisons suivre ce premier symbole par un point pour passer à l'objet suivant. Puis, par le nom de clé suivant et ainsi de suite jusqu'à la clé recherchée, à laquelle nous collons un `[1]` pour la 2ᵉ valeur de la liste (les valeurs commencent à 0). Pour plus d'informations sur les `json_path`, vous pouvez consulter la [documentation](https://www.postgresql.org/docs/current/functions-json.html#FUNCTIONS-SQLJSON-PATH) ## Attaquons nous au recensement From 52d16a7b88e9e7e5e72327fb7701d6e680798225 Mon Sep 17 00:00:00 2001 From: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> Date: Tue, 14 Jan 2025 19:53:43 +0100 Subject: [PATCH 044/101] Update content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Loïc Bartoletti Signed-off-by: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> --- .../2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md index 4db7725166..6d6b86f110 100644 --- a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md +++ b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md @@ -294,11 +294,11 @@ Pour plus d'informations sur les `json_path`, vous pouvez consulter la [document ## Attaquons nous au recensement -Bien, maintenant que nous avons essayé d'expliquer tant bien que mal les concepts avec des exemples simples car il faut bien commencer quelque part, essayons avec quelque chose d'un peu plus volumineux. +Bien, maintenant que nous avons essayé d'expliquer tant bien que mal les concepts avec des exemples simples, car il faut bien commencer quelque part, essayons avec quelque chose d'un peu plus volumineux. -Récupérons le dernier millésime du volet population du recensement communal [au format csv sur le site de l'INSEE](https://www.insee.fr/fr/information/8183122) (vous voulez les bases des principaux indicateurs). Pour l'exemple, on utilisera le fichier "Evolution et structure de la population". Il faut tout d'abord nettoyer les noms de champs car l'INSEE indique le millésime systématiquement dans ses noms de champs, ce qui fait que ces derniers changent tous les ans pour un même indicateur. +Récupérons le dernier millésime du volet population du recensement communal [au format csv sur le site de l'INSEE](https://www.insee.fr/fr/information/8183122) (vous voulez les bases des principaux indicateurs). Pour l'exemple, on utilisera le fichier "Evolution et structure de la population". Il faut tout d'abord nettoyer les noms de champ. En effet, l'INSEE indique le millésime systématiquement dans ses noms de champ, ce qui fait que ces derniers changent tous les ans pour un même indicateur. -Les noms de champs commencent tous par P ou C, ceci indique *exploitation principale* (réponses brutes aux questions du recensement) ou *exploitation complémentaire* (croisement de réponses pour établir un indicateur). Les champs provenant de l'exploitation principale et ceux issus de la complémentaire ne doivent pas êtres croisés entre eux. Cette information est évidemmment a conserver mais par choix personnel je préfère la mettre à la fin plutôt qu'au début du nom. +Les noms de champ commencent tous par P ou C, ceci indique *exploitation principale* (réponses brutes aux questions du recensement) ou *exploitation complémentaire* (croisement de réponses pour établir un indicateur). Les champs provenant de l'exploitation principale et ceux issus de la complémentaire ne doivent pas être croisés entre eux. Cette information est évidemment à conserver, mais par choix personnel, je préfère la mettre à la fin plutôt qu'au début du nom. Vous trouverez [ici](https://github.com/thomas-szczurek/base_donnees_insee/blob/main/sql/import/correction_champs_insee.xlsx) un tableur dont le rôle est de s'occuper de tout ça. From 7bf3aa8232adbe789f8d78ebdbccf305c3711cfa Mon Sep 17 00:00:00 2001 From: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> Date: Tue, 14 Jan 2025 19:59:02 +0100 Subject: [PATCH 045/101] Update content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md Signed-off-by: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> --- .../2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md index 6d6b86f110..6bd5669683 100644 --- a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md +++ b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md @@ -226,7 +226,7 @@ INSERT INTO insee.donnees_communes (code_commune, annee, fk_base, donnees) VALUE '99999', 2024, 1, - json_object( + jsonb_object( string_to_array('tomates,42,melons,12',',') ) ) From ea1d797b80e933742330baaa16e72197eb28c938 Mon Sep 17 00:00:00 2001 From: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> Date: Sat, 18 Jan 2025 14:51:34 +0100 Subject: [PATCH 046/101] Update content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Loïc Bartoletti Signed-off-by: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> --- .../2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md index 6bd5669683..9650009428 100644 --- a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md +++ b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md @@ -21,7 +21,7 @@ tags: :calendar: Date de publication initiale : {{ page.meta.date | date_localized }} -Dans le cadre d'un projet personnel encadré de type "tu l'as fait pour les iris de Lille avec Sqlite, t'as vu que tu sais faire. Bon bah maintenant tu le fait pour toutes les communes de France dans PG", j'ai voulu stocker une bonne partie des données du recensement de l'Insee dans une base PostgreSQL avec des tables multimillésimes. Problème, même au sein d'un même jeu de données, les champs peuvent changer au cours des années et celà empêche de pouvoir dégager une structure de table fixe, ce qui est assez génant vous en conviendrez. La solution ? Passer par des données semi-structurées, soit stocker ces données en JSON dans le champ d'une table. Cet article se veut un condensé de cette expérience. +Dans le cadre d'un projet personnel encadré de type "tu l'as fait pour les IRIS de Lille avec Sqlite, t'as vu que tu sais faire. Bon bah maintenant tu le fais pour toutes les communes de France dans PG", j'ai voulu stocker une bonne partie des données du recensement de l'Insee dans une base PostgreSQL avec des tables multimillésimes. Problème, même au sein d'un même jeu de données, les champs peuvent changer au cours des années et cela empêchent de pouvoir dégager une structure de table fixe, ce qui est assez gênant vous en conviendrez. La solution ? Passer par des données semi-structurées, soit stocker ces données en JSON dans le champ d'une table. Cet article se veut un condensé de cette expérience. !!! warning Ces travaux ont été réalisés avant la sortie de PostgreSQL 17 qui ajoute d'importantes fonctionnalités pour le JSON avec les [`JSON_TABLE`](https://doc.postgresql.fr/17/functions-json.html#FUNCTIONS-SQLJSON-TABLE), elles ne seront pas évoquées ici. From 3994b822f95dc3a615e79c21651238fa127a5e66 Mon Sep 17 00:00:00 2001 From: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> Date: Sat, 18 Jan 2025 14:54:41 +0100 Subject: [PATCH 047/101] Update content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md Signed-off-by: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> --- .../2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md index 9650009428..4859410322 100644 --- a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md +++ b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md @@ -21,7 +21,7 @@ tags: :calendar: Date de publication initiale : {{ page.meta.date | date_localized }} -Dans le cadre d'un projet personnel encadré de type "tu l'as fait pour les IRIS de Lille avec Sqlite, t'as vu que tu sais faire. Bon bah maintenant tu le fais pour toutes les communes de France dans PG", j'ai voulu stocker une bonne partie des données du recensement de l'Insee dans une base PostgreSQL avec des tables multimillésimes. Problème, même au sein d'un même jeu de données, les champs peuvent changer au cours des années et cela empêchent de pouvoir dégager une structure de table fixe, ce qui est assez gênant vous en conviendrez. La solution ? Passer par des données semi-structurées, soit stocker ces données en JSON dans le champ d'une table. Cet article se veut un condensé de cette expérience. +Dans le cadre d'un projet personnel encadré de type "tu l'as fait pour les IRIS de Lille avec Sqlite, t'as vu que tu sais faire. Bon bah maintenant tu le fais pour toutes les communes de France dans PG", j'ai voulu stocker une bonne partie des données du recensement de l'Insee dans une base PostgreSQL avec des tables multimillésimes. Problème, même au sein d'un même jeu de données, les champs peuvent changer au cours des années et cela empêche de pouvoir dégager une structure de table fixe, ce qui est assez gênant vous en conviendrez. La solution ? Passer par des données semi-structurées, soit stocker ces données en JSON dans le champ d'une table. Cet article se veut un condensé de cette expérience. !!! warning Ces travaux ont été réalisés avant la sortie de PostgreSQL 17 qui ajoute d'importantes fonctionnalités pour le JSON avec les [`JSON_TABLE`](https://doc.postgresql.fr/17/functions-json.html#FUNCTIONS-SQLJSON-TABLE), elles ne seront pas évoquées ici. From 756e8c22096d199914992bdfe87dc42d14fe2d8d Mon Sep 17 00:00:00 2001 From: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> Date: Sat, 18 Jan 2025 15:04:08 +0100 Subject: [PATCH 048/101] Apply suggestions from code review Signed-off-by: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> --- .../2025-01-21_travailler-avec-JSON-et-PostgreSQL.md | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md index 4859410322..f8733cb282 100644 --- a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md +++ b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md @@ -130,6 +130,11 @@ Pour les aventuriers et aventurières, il existe une extention de PostgreSQL `bt Je vais éviter de vous spammer du DDL, mais vous pourrez retrouver le schéma complet de la base [ici](https://github.com/thomas-szczurek/base_donnees_insee/tree/main/sql/creation_tables) +En voici cependant un schéma succinct pour pour aider à la compréhension du reste de l'article : + +![modele_de_donnees](https://cdn.geotribu.fr/img/articles-blog-rdp/articles/2024/postgresql_json/modele.png){: .img-center loading=lazy } + + Partant d'un schéma nommé `insee`, on va créer deux tables. La première contiendra la liste des *bases* disponibles, les différents volets du recensement. Une seconde permettant de stocker les données ; pour rester concentré sur le json, on va s'épargner 95% du modèle sous-jacent. On ne gérera donc par ici les codes communes, etc. En bonus pour celles et ceux voulant tester ce que Postgres a dans le ventre, je proposerai une variante juste en dessous : ```sql @@ -243,7 +248,7 @@ Cette requête encodera cet objet json dans le champs `données` : Maintenant comment récupérer notre nombre de melons pour le code commune 99999 en 2024 ? Celà se fait grace à des opérateurs spéciaux : -- `champ_jsonb -> 'clé'` récupère la valeur d'une clé en concervant son type json +- `champ_jsonb -> 'clé'` récupère la valeur d'une clé en conservant son type json - `champ_jsonb ->> 'clé'` fait de même en transformant la valeur en type sql "classique" ```sql @@ -538,14 +543,14 @@ SELECT * FROM final WHERE valeur IS NOT NULL; Attention, la création de cette vue matérialisée ou son refresh peut prendre un certain temps si vous avez stocké beaucoup de données (1 heure chez moi pour les 6 volets de 2015 à 2021). -Enfin, histoire de vivre avec son temps et non comme un viel ours des cavernes ou comme un data machin de collectivité territoriale (j'y bosse, j'ai le droit), on va convertir cette vue matérialisée en fichier [parquet](https://parquet.apache.org/). +Enfin, histoire de vivre avec son temps et non comme un viel ours des cavernes, on va convertir cette vue matérialisée en fichier [parquet](https://parquet.apache.org/). Et, pour ça, on va utiliser gdal/ogr qui est décidément incroyable. ```sh ogr2ogr -of parquet donnees_insee.parquet PG:"dbname='insee' shcema='insee' tables='donnees_communes_olap' user='nom_utilisateur' password='votre_mot_de_passe'" ``` -Et on peut ainsi mettre le fichier sur un espace cloud, comme [ici](https://donnees-insee.s3.fr-par.scw.cloud/donnees_insee_olap.parquet) ! Vous pouvez ensuite sortir votre plus beau générateur de publications Linkedin qui mettra plein d'emojis choupi et faire le cake sur les rezos (imaginez que 90% du contenu de Linkedin doit être fait avec ces trucs qui sont capable de vous générer des publications expliquant que le shape a des avantages sur le geopackage, le tout sur un ton très assuré). +Et on peut ainsi mettre le fichier sur un espace cloud, comme [ici](https://donnees-insee.s3.fr-par.scw.cloud/donnees_insee_olap.parquet) ! Vous pouvez ensuite sortir votre plus beau générateur de publications Linkedin qui mettra plein d'emojis choupi et faire le cake sur les rezos (imaginez que 90% du contenu de Linkedin doit être fait avec ces trucs qui sont capable de vous générer des publications expliquant que l'un des rares avantages du shape sur le geopackage est d'être un format multi-fichiers, le tout sur un ton très assuré). From 1fa3b702564d30e170baade45185ae4d8bc5a94e Mon Sep 17 00:00:00 2001 From: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> Date: Sat, 18 Jan 2025 15:04:35 +0100 Subject: [PATCH 049/101] Update content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Loïc Bartoletti Signed-off-by: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> --- .../2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md index f8733cb282..b7b9fea993 100644 --- a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md +++ b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md @@ -488,9 +488,9 @@ ORDER BY fk_base, clef_json; ![vm_presence_cles_annees](https://cdn.geotribu.fr/img/articles-blog-rdp/articles/2024/postgresql_json/vm_presence_cles_annees.png){: .img-center loading=lazy } -La seule difficulté ici est la présence de [LATERAL](https://docs.postgresql.fr/17/queries-table-expressions.html#QUERIES-FROM). Ce "mot" permet d'utiliser un champ de la requête principale dans une sous requête placée dans une clause FROM. Les éléments de la sous requête seront ensuite évalués atomiquement avant d'être joins à la table d'origine. Oui, ce n'est pas très facile à expliquer. Ici, le WHERE de la sous requête va interroger le champ données de la table "donnees_communes" pour voir si il y voit la clef_json actuellement en train d'être évaluée dans la table "correspondance_clefs_champs" possédant l'alias "c". Si oui, alors on prend la valeur minimum/maximum du champ année, et on le joint à cette ligne et uniquement cette ligne. Puis évaluation de clef_json suivante ... (*évalués atomiquement*) +La seule difficulté ici est la présence de [LATERAL](https://docs.postgresql.fr/17/queries-table-expressions.html#QUERIES-FROM). Ce « mot » permet d'utiliser un champ de la requête principale dans une sous-requête placée dans une clause FROM. Les éléments de la sous-requête seront ensuite évalués atomiquement avant d'être joints à la table d'origine. Oui, ce n'est pas très facile à expliquer. Ici, le WHERE de la sous-requête va interroger le champ donné de la table "donnees_communes" pour voir s'il y voit la clef_json en train d'être évaluée dans la table "correspondance_clefs_champs" possédant l'alias "c". Si oui, alors on prend la valeur minimum/maximum du champ année et on le joint à cette ligne et uniquement à cette ligne. Puis évaluation de clef_json suivante ... (*évaluée atomiquement*) -Maintenant, on voudrait proposer un produit un peu plus simple d'utilisation avec tout ça. Désolé d'avance, je vais copier une requête de 50 lignes une seconde fois. La seule table utilisée ici que nous n'avons pas créé est une table zonages_administratifs comprenant les codes communes dans un champ "code_admin", et un champ "fk_type" contenant le type de zonage administratif (1 pour les communes). +Maintenant, on voudrait proposer un produit un peu plus simple d'utilisation avec tout ça. Excusez-moi d'avance, je copierai une requête de 50 lignes une seconde fois. La seule table utilisée ici que nous n'avons pas créée est une table zonages_administratifs comprenant les codes communes dans un champ "code_admin", et un champ "fk_type" contenant le type de zonage administratif (1 pour les communes). ```sql CREATE MATERIALIZED VIEW insee.donnees_communes_olap AS From b2ec37c576bbdb0aba85e79dc9c3f90f289baa93 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 18 Jan 2025 14:04:38 +0000 Subject: [PATCH 050/101] =?UTF-8?q?[pre-commit.ci]=20Corrections=20automat?= =?UTF-8?q?iques=20appliqu=C3=A9es=20par=20les=20git=20hooks.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md | 1 - 1 file changed, 1 deletion(-) diff --git a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md index b7b9fea993..82ed52e7b7 100644 --- a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md +++ b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md @@ -134,7 +134,6 @@ En voici cependant un schéma succinct pour pour aider à la compréhension du r ![modele_de_donnees](https://cdn.geotribu.fr/img/articles-blog-rdp/articles/2024/postgresql_json/modele.png){: .img-center loading=lazy } - Partant d'un schéma nommé `insee`, on va créer deux tables. La première contiendra la liste des *bases* disponibles, les différents volets du recensement. Une seconde permettant de stocker les données ; pour rester concentré sur le json, on va s'épargner 95% du modèle sous-jacent. On ne gérera donc par ici les codes communes, etc. En bonus pour celles et ceux voulant tester ce que Postgres a dans le ventre, je proposerai une variante juste en dessous : ```sql From 02e1d144e58bd6ed7e627d2cac4d82442dd7510a Mon Sep 17 00:00:00 2001 From: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> Date: Sat, 18 Jan 2025 15:05:49 +0100 Subject: [PATCH 051/101] Update content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md Signed-off-by: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> --- .../2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md index 82ed52e7b7..f646eddc15 100644 --- a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md +++ b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md @@ -487,7 +487,7 @@ ORDER BY fk_base, clef_json; ![vm_presence_cles_annees](https://cdn.geotribu.fr/img/articles-blog-rdp/articles/2024/postgresql_json/vm_presence_cles_annees.png){: .img-center loading=lazy } -La seule difficulté ici est la présence de [LATERAL](https://docs.postgresql.fr/17/queries-table-expressions.html#QUERIES-FROM). Ce « mot » permet d'utiliser un champ de la requête principale dans une sous-requête placée dans une clause FROM. Les éléments de la sous-requête seront ensuite évalués atomiquement avant d'être joints à la table d'origine. Oui, ce n'est pas très facile à expliquer. Ici, le WHERE de la sous-requête va interroger le champ donné de la table "donnees_communes" pour voir s'il y voit la clef_json en train d'être évaluée dans la table "correspondance_clefs_champs" possédant l'alias "c". Si oui, alors on prend la valeur minimum/maximum du champ année et on le joint à cette ligne et uniquement à cette ligne. Puis évaluation de clef_json suivante ... (*évaluée atomiquement*) +La seule difficulté ici est la présence de [LATERAL](https://docs.postgresql.fr/17/queries-table-expressions.html#QUERIES-FROM). Ce « mot » permet d'utiliser un champ de la requête principale dans une sous-requête placée dans une clause FROM. Les éléments de la sous-requête seront ensuite évalués atomiquement avant d'être joints à la table d'origine. Oui, ce n'est pas très facile à expliquer. Ici, le WHERE de la sous-requête va interroger le champ `donnees` de la table "donnees_communes" pour voir s'il y voit la clef_json en train d'être évaluée dans la table "correspondance_clefs_champs" possédant l'alias "c". Si oui, alors on prend la valeur minimum/maximum du champ année et on le joint à cette ligne et uniquement à cette ligne. Puis évaluation de clef_json suivante ... (*évaluée atomiquement*) Maintenant, on voudrait proposer un produit un peu plus simple d'utilisation avec tout ça. Excusez-moi d'avance, je copierai une requête de 50 lignes une seconde fois. La seule table utilisée ici que nous n'avons pas créée est une table zonages_administratifs comprenant les codes communes dans un champ "code_admin", et un champ "fk_type" contenant le type de zonage administratif (1 pour les communes). From 0eea18359a4a363d309965e72f3d7db72128f635 Mon Sep 17 00:00:00 2001 From: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> Date: Sat, 18 Jan 2025 15:07:52 +0100 Subject: [PATCH 052/101] Update content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md Signed-off-by: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> --- .../2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md index f646eddc15..eefd68b215 100644 --- a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md +++ b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md @@ -130,7 +130,7 @@ Pour les aventuriers et aventurières, il existe une extention de PostgreSQL `bt Je vais éviter de vous spammer du DDL, mais vous pourrez retrouver le schéma complet de la base [ici](https://github.com/thomas-szczurek/base_donnees_insee/tree/main/sql/creation_tables) -En voici cependant un schéma succinct pour pour aider à la compréhension du reste de l'article : +En voici cependant un schéma succinct pour aider à la compréhension du reste de l'article : ![modele_de_donnees](https://cdn.geotribu.fr/img/articles-blog-rdp/articles/2024/postgresql_json/modele.png){: .img-center loading=lazy } From e90598248eeb318cc4363415938f493a29bfc955 Mon Sep 17 00:00:00 2001 From: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> Date: Sat, 18 Jan 2025 15:31:35 +0100 Subject: [PATCH 053/101] Update content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md Signed-off-by: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> --- .../2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md index eefd68b215..d5c2a3d1eb 100644 --- a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md +++ b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md @@ -549,7 +549,7 @@ Et, pour ça, on va utiliser gdal/ogr qui est décidément incroyable. ogr2ogr -of parquet donnees_insee.parquet PG:"dbname='insee' shcema='insee' tables='donnees_communes_olap' user='nom_utilisateur' password='votre_mot_de_passe'" ``` -Et on peut ainsi mettre le fichier sur un espace cloud, comme [ici](https://donnees-insee.s3.fr-par.scw.cloud/donnees_insee_olap.parquet) ! Vous pouvez ensuite sortir votre plus beau générateur de publications Linkedin qui mettra plein d'emojis choupi et faire le cake sur les rezos (imaginez que 90% du contenu de Linkedin doit être fait avec ces trucs qui sont capable de vous générer des publications expliquant que l'un des rares avantages du shape sur le geopackage est d'être un format multi-fichiers, le tout sur un ton très assuré). +Et on peut ainsi mettre le fichier sur un espace cloud, comme [ici](https://donnees-insee.s3.fr-par.scw.cloud/donnees_insee_olap.parquet) ! Vous pouvez ensuite sortir votre plus beau générateur de publications Linkedin qui mettra plein d'emojis choupi et faire le cake sur les rezos (imaginez que 90% du contenu de Linkedin doit être fait avec ces trucs qui sont capable de vous générer des publications expliquant que l'un des rares avantages du shape sur le geopackage est d'être un format multi-fichiers, le tout sur un ton très assuré). From f0de7c957c7450737f80e5271d731c7675dd8a66 Mon Sep 17 00:00:00 2001 From: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> Date: Sun, 19 Jan 2025 13:30:44 +0100 Subject: [PATCH 054/101] Update content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Michaël Galien <64089998+Michael-cd30@users.noreply.github.com> Signed-off-by: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> --- .../2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md index d5c2a3d1eb..0107adf945 100644 --- a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md +++ b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md @@ -30,7 +30,7 @@ Puisque nous allons parler de json et de données semi-structurées, je me sens **Le modèle relationnel, c'est bon, mangez-en, et les contraintes d'intégrités ont été inventées pour de bonnes raisons.** -Cet article ne se veut surtout pas être une invitation à partir en mode yolo sur la gestion des données "c'est bon ya qu'a tout mettre en json" (comme un vulgaire dev qui mettrait tout dans Mongodb diraient les mauvaises langues). +Cet article ne se veut surtout pas être une invitation à partir en mode yolo sur la gestion des données "c'est bon ya qu'a tout mettre en json" (comme un vulgaire dev qui mettrait tout dans MongoDB diraient les mauvaises langues). ## Le JSON pour les débutant.es From fea32312a474756bbd6abe011877634f85d7eacd Mon Sep 17 00:00:00 2001 From: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> Date: Sun, 19 Jan 2025 13:31:08 +0100 Subject: [PATCH 055/101] Update content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Michaël Galien <64089998+Michael-cd30@users.noreply.github.com> Signed-off-by: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> --- .../2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md index 0107adf945..5fe189500a 100644 --- a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md +++ b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md @@ -449,7 +449,7 @@ Ouf. ![donnees_communes](https://cdn.geotribu.fr/img/articles-blog-rdp/articles/2024/postgresql_json/donnees_communes.png){: .img-center loading=lazy } -On veillera à bien grouper les inserts dans cet ordre annee/base/code_commune afin de faciliter la lecture des données par PostgreSQL (si votre table est partitionnée, celà sera facilité). +On veillera à bien grouper les inserts dans cet ordre annee/base/code_commune afin de faciliter la lecture des données par PostgreSQL (si votre table est partitionnée, cela sera facilité). Maintenant, imaginez que comme l'auteur de ces lignes, vos gros doigts boudinnés cafouillent et glissent sur les touches lors de la rédaction de cette requête, qu'une faute de frappe s'y glisse, et là c'est le drame. Comment modifier le nom d'une clé déjà encodée dans la table ? Avec cette astuce : From f2ee32030bccc4fe9d54876ed55caceb9718c4cb Mon Sep 17 00:00:00 2001 From: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> Date: Sun, 19 Jan 2025 13:33:27 +0100 Subject: [PATCH 056/101] Update content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Michaël Galien <64089998+Michael-cd30@users.noreply.github.com> Signed-off-by: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> --- .../2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md index 5fe189500a..95b6d0a9cb 100644 --- a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md +++ b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md @@ -542,7 +542,7 @@ SELECT * FROM final WHERE valeur IS NOT NULL; Attention, la création de cette vue matérialisée ou son refresh peut prendre un certain temps si vous avez stocké beaucoup de données (1 heure chez moi pour les 6 volets de 2015 à 2021). -Enfin, histoire de vivre avec son temps et non comme un viel ours des cavernes, on va convertir cette vue matérialisée en fichier [parquet](https://parquet.apache.org/). +Enfin, histoire de vivre avec son temps et non comme un vieil ours des cavernes, on va convertir cette vue matérialisée en fichier [parquet](https://parquet.apache.org/). Et, pour ça, on va utiliser gdal/ogr qui est décidément incroyable. ```sh From a7117de70172d1ac5927eeef719f8635c28d7ec4 Mon Sep 17 00:00:00 2001 From: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> Date: Sun, 19 Jan 2025 13:33:49 +0100 Subject: [PATCH 057/101] Update content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md Signed-off-by: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> --- .../2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md index 95b6d0a9cb..7572b99799 100644 --- a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md +++ b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md @@ -34,7 +34,7 @@ Cet article ne se veut surtout pas être une invitation à partir en mode yolo s ## Le JSON pour les débutant.es -Pour celles et ceux qui ne connaisent pas le `json`, il s'agit d'un format textuel de représentation des données fonctionnant en partie sur un système de `clé : valeur` qu'on peut voir comme une sorte d'évolution du `xml`. +Pour celles et ceux qui ne connaissent pas le `json`, il s'agit d'un format textuel de représentation des données venant du Java Script fonctionnant en partie sur un système de `clé : valeur` qu'on peut voir comme une sorte d'évolution du `xml`. ```json {"clé_1": "valeur", "clé_2": "valeur", "clé_3": "valeur"} From 089e8177df66db4f518df8cbdeae097e16a999e5 Mon Sep 17 00:00:00 2001 From: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> Date: Sun, 19 Jan 2025 13:34:04 +0100 Subject: [PATCH 058/101] Update content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md Signed-off-by: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> --- .../2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md | 1 - 1 file changed, 1 deletion(-) diff --git a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md index 7572b99799..cf1ca2b836 100644 --- a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md +++ b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md @@ -481,7 +481,6 @@ SELECT d.derniere AS derniere_annee_presence FROM insee.correspondance_clefs_champs AS c, LATERAL (SELECT min(annee) AS premiere FROM insee.donnees_communes WHERE fk_base = c.fk_base AND donnees ? c.clef_json) AS p, - LATERAL (SELECT max(annee) AS derniere FROM insee.donnees_communes WHERE fk_base = c.fk_base AND donnees ? c.clef_json) AS d ORDER BY fk_base, clef_json; ``` From f352792b7accb810bd711a57d83264713c90788b Mon Sep 17 00:00:00 2001 From: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> Date: Sun, 19 Jan 2025 13:34:17 +0100 Subject: [PATCH 059/101] Update content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md Signed-off-by: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> --- .../2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md index cf1ca2b836..165e148248 100644 --- a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md +++ b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md @@ -480,7 +480,7 @@ SELECT p.premiere AS premiere_annee_presence, d.derniere AS derniere_annee_presence FROM insee.correspondance_clefs_champs AS c, - LATERAL (SELECT min(annee) AS premiere FROM insee.donnees_communes WHERE fk_base = c.fk_base AND donnees ? c.clef_json) AS p, + LATERAL (SELECT min(annee) AS premiere FROM insee.donnees_communes WHERE fk_base = c.fk_base AND donnees ? c.clef_json) AS a ORDER BY fk_base, clef_json; ``` From 4ea0fcab0df6c7bff4958d36399cccd3f94cac07 Mon Sep 17 00:00:00 2001 From: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> Date: Sun, 19 Jan 2025 13:34:31 +0100 Subject: [PATCH 060/101] Update content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md Signed-off-by: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> --- .../2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md index 165e148248..f14d3e59f8 100644 --- a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md +++ b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md @@ -478,7 +478,7 @@ SELECT c.clef_json AS clef_json, c.fk_base AS fk_base, p.premiere AS premiere_annee_presence, - d.derniere AS derniere_annee_presence + a.derniere AS derniere_annee_presence FROM insee.correspondance_clefs_champs AS c, LATERAL (SELECT min(annee) AS premiere FROM insee.donnees_communes WHERE fk_base = c.fk_base AND donnees ? c.clef_json) AS a ORDER BY fk_base, clef_json; From bea02b77a7d835d8f3eeada7c054cf7b01bc202a Mon Sep 17 00:00:00 2001 From: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> Date: Sun, 19 Jan 2025 13:34:44 +0100 Subject: [PATCH 061/101] Update content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md Signed-off-by: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> --- .../2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md index f14d3e59f8..49abfffb7e 100644 --- a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md +++ b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md @@ -128,7 +128,7 @@ Pour les aventuriers et aventurières, il existe une extention de PostgreSQL `bt ## Création des tables -Je vais éviter de vous spammer du DDL, mais vous pourrez retrouver le schéma complet de la base [ici](https://github.com/thomas-szczurek/base_donnees_insee/tree/main/sql/creation_tables) +Je vais éviter de vous spammer du `DDL` ([Data Definition Language](https://fr.wikipedia.org/wiki/Langage_de_d%C3%A9finition_de_donn%C3%A9es), mais vous pourrez retrouver le schéma complet de la base [ici](https://github.com/thomas-szczurek/base_donnees_insee/tree/main/sql/creation_tables) En voici cependant un schéma succinct pour aider à la compréhension du reste de l'article : From 3ec2caa59771bb305203521d6e07554e48511ad1 Mon Sep 17 00:00:00 2001 From: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> Date: Sun, 19 Jan 2025 13:35:05 +0100 Subject: [PATCH 062/101] Update content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md Signed-off-by: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> --- .../2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md index 49abfffb7e..652106fe99 100644 --- a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md +++ b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md @@ -437,7 +437,7 @@ WITH d AS ( -- Conversion des chaines de texte en json et insertion dans la table INSERT INTO insee.donnees_communes("code_commune","annee","fk_base","donnees") SELECT - "CODGEO", + CODGEO, 2021, 1, jsonb_object(string_to_array(data::text,',')) From dfd642669c07beb06707295d54424e9330549213 Mon Sep 17 00:00:00 2001 From: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> Date: Sun, 19 Jan 2025 13:35:20 +0100 Subject: [PATCH 063/101] Update content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md Signed-off-by: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> --- .../2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md index 652106fe99..f0c76cb144 100644 --- a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md +++ b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md @@ -477,7 +477,7 @@ SELECT c.pk_id AS pk_id, c.clef_json AS clef_json, c.fk_base AS fk_base, - p.premiere AS premiere_annee_presence, + a.premiere AS premiere_annee_presence, a.derniere AS derniere_annee_presence FROM insee.correspondance_clefs_champs AS c, LATERAL (SELECT min(annee) AS premiere FROM insee.donnees_communes WHERE fk_base = c.fk_base AND donnees ? c.clef_json) AS a From bd5fcac9c00ada7a0465033a12be63d52fc062ea Mon Sep 17 00:00:00 2001 From: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> Date: Sun, 19 Jan 2025 13:35:35 +0100 Subject: [PATCH 064/101] Update content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md Signed-off-by: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> --- .../2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md index f0c76cb144..a609ab05fd 100644 --- a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md +++ b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md @@ -256,6 +256,8 @@ SELECT FROM insee.donnees_communes WHERE donnees ? 'melons' + AND code_commune = '99999' + AND annee = 2024 ``` Vous pouvez voir que j'utilise l'opérateur `?`; uniquement valable pour les champs `jsonb` et non ceux en simple `json`. En effet, lorsque l'on requête un champ json/jsonb, un retour vous est fait pour l'ensemble des enregistrements de la table, même ceux ne contenant pas la clé. Comprendre que si votre table comprend 100 000 enregistrements, mais que seuls 100 contiennent la clé "melons", ne pas spécifier cette clause WHERE vous renverrait 100 000 lignes, dont 99 900 de `NULL`.`?` est un opérateur json permettant de poser la question "la clé est-elle présente au premier niveau du champ json pour cet enregistrement ?", et on ne récupérerait que nos 100 enregistrements contenant la clé "melons". From 61a8d7506772ac965bd8a86cc8a8a9abcd3d56e8 Mon Sep 17 00:00:00 2001 From: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> Date: Sun, 19 Jan 2025 13:35:50 +0100 Subject: [PATCH 065/101] Update content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md Signed-off-by: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> --- .../2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md index a609ab05fd..8943e3c449 100644 --- a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md +++ b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md @@ -287,6 +287,8 @@ INSERT INTO test.test (donnees) VALUES ('{"cle1": "valeur1", "cle2": ["foo", "bar", "baz"]}'::jsonb) ``` +#### Recupération + Pour récupérer une valeur, on utilise la fonction `jsonb_path_query()` qui possède deux paramètres : `le nom du champ` contenant les données json, et le `json_path` vers la valeur à atteindre. Imaginons que nous voulions récupérer la deuxième valeur de la liste contenue dans "cle2" : ```sql From 64affb5ee1f43b8a14e80212ebd2dbaa8f5413d5 Mon Sep 17 00:00:00 2001 From: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> Date: Sun, 19 Jan 2025 13:36:03 +0100 Subject: [PATCH 066/101] Update content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md Signed-off-by: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> --- .../2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md index 8943e3c449..83a27bb474 100644 --- a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md +++ b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md @@ -21,7 +21,7 @@ tags: :calendar: Date de publication initiale : {{ page.meta.date | date_localized }} -Dans le cadre d'un projet personnel encadré de type "tu l'as fait pour les IRIS de Lille avec Sqlite, t'as vu que tu sais faire. Bon bah maintenant tu le fais pour toutes les communes de France dans PG", j'ai voulu stocker une bonne partie des données du recensement de l'Insee dans une base PostgreSQL avec des tables multimillésimes. Problème, même au sein d'un même jeu de données, les champs peuvent changer au cours des années et cela empêche de pouvoir dégager une structure de table fixe, ce qui est assez gênant vous en conviendrez. La solution ? Passer par des données semi-structurées, soit stocker ces données en JSON dans le champ d'une table. Cet article se veut un condensé de cette expérience. +Dans le cadre d'un projet personnel, j'ai voulu stocker une bonne partie des données du recensement de l'Insee dans une base PostgreSQL avec des tables multimillésimes. Problème, au sein d'un même jeu de données, les champs peuvent changer au cours des années et cela empêche de pouvoir dégager une structure de table fixe, ce qui est assez gênant vous en conviendrez. La solution ? Passer par des données semi-structurées, soit stocker ces données en JSON dans le champ d'une table. Cet article se veut un condensé de cette expérience. !!! warning Ces travaux ont été réalisés avant la sortie de PostgreSQL 17 qui ajoute d'importantes fonctionnalités pour le JSON avec les [`JSON_TABLE`](https://doc.postgresql.fr/17/functions-json.html#FUNCTIONS-SQLJSON-TABLE), elles ne seront pas évoquées ici. From 368ba5180ce662b2ff2239d511db87c45f5b911a Mon Sep 17 00:00:00 2001 From: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> Date: Sun, 19 Jan 2025 13:37:03 +0100 Subject: [PATCH 067/101] Update content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Michaël Galien <64089998+Michael-cd30@users.noreply.github.com> Signed-off-by: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> --- .../2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md index 83a27bb474..9d1fb1f285 100644 --- a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md +++ b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md @@ -134,7 +134,7 @@ En voici cependant un schéma succinct pour aider à la compréhension du reste ![modele_de_donnees](https://cdn.geotribu.fr/img/articles-blog-rdp/articles/2024/postgresql_json/modele.png){: .img-center loading=lazy } -Partant d'un schéma nommé `insee`, on va créer deux tables. La première contiendra la liste des *bases* disponibles, les différents volets du recensement. Une seconde permettant de stocker les données ; pour rester concentré sur le json, on va s'épargner 95% du modèle sous-jacent. On ne gérera donc par ici les codes communes, etc. En bonus pour celles et ceux voulant tester ce que Postgres a dans le ventre, je proposerai une variante juste en dessous : +Partant d'un schéma nommé `insee`, on va créer deux tables. La première contiendra la liste des *bases* disponibles, les différents volets du recensement. Une seconde permettant de stocker les données ; pour rester concentré sur le json, on va s'épargner 95% du modèle sous-jacent. On ne gérera donc pas ici les codes communes, etc. En bonus pour celles et ceux voulant tester ce que Postgres a dans le ventre, je proposerai une variante juste en dessous : ```sql BEGIN; From 7c551fda59122e008b4265ec1dbaa6ca769c5ccf Mon Sep 17 00:00:00 2001 From: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> Date: Sun, 19 Jan 2025 13:37:20 +0100 Subject: [PATCH 068/101] Update content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Michaël Galien <64089998+Michael-cd30@users.noreply.github.com> Signed-off-by: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> --- .../2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md index 9d1fb1f285..bbf3d4effb 100644 --- a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md +++ b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md @@ -165,7 +165,7 @@ END; Les plus tatillons des DBA d'entre vous auront remarqué ce `CHECK`. "Mais pourquoi qu'il utilise pas varchar(5) ? Il fait vraiment n'importe quoi !" Tout simplement, car cette forme permet d'utiliser un type de texte aux nombres de caractère **réellement** arbitraire (le type `text`) contrairement à varchar(255), tout en pouvant en contrôler le nombre minimum et maximum avec le prédicat `CHECK` (contrairement à varchar qui ne contrôle que le maximum) comme expliqué sur le [wiki Postgres](https://wiki.postgresql.org/wiki/Don%27t_Do_This#Don.27t_use_varchar.28n.29_by_default). -Et on insère quelques lignes dans notre table insee.bases +Et on insère quelques lignes dans notre table insee.bases : ```sql INSERT INTO insee.bases (nom) VALUES From 8ebcbf035e33fb6d3864349fbdac96104ac92804 Mon Sep 17 00:00:00 2001 From: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> Date: Sun, 19 Jan 2025 13:38:23 +0100 Subject: [PATCH 069/101] Update content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Michaël Galien <64089998+Michael-cd30@users.noreply.github.com> Signed-off-by: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> --- .../2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md index bbf3d4effb..43c3dc2fe6 100644 --- a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md +++ b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md @@ -222,7 +222,7 @@ Pour passer des données textuelles / SQL vers des données encodées en JSON da ### Exemple simple -On va créer une chaine de texte qui contiendra le contenu de notre json dont les valeurs seront séparées par des virgules `clé1,valeur1, clé2, valeur2`. Cette chaine sera passée dans une fonction `string_to_array()` la transformant en `array` avec comme séparateur des `,` pour séparer les éléments de la chaine de texte vers des éléments de liste, caractère passé en second paramètre de la fonction. Cet `array` sera ensuite envoyé dans la fonction `jsonb_object()`. +On va créer une chaine de texte qui contiendra le contenu de notre json dont les valeurs seront séparées par des virgules `clé1, valeur1, clé2, valeur2`. Cette chaine sera passée dans une fonction `string_to_array()` la transformant en `array` avec comme séparateur des `,` pour séparer les éléments de la chaine de texte vers des éléments de liste, caractère passé en second paramètre de la fonction. Cet `array` sera ensuite envoyé dans la fonction `jsonb_object()`. ```sql INSERT INTO insee.donnees_communes (code_commune, annee, fk_base, donnees) VALUES From f2b24b1d79a0287184b41dabb9f9bdee61b20a49 Mon Sep 17 00:00:00 2001 From: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> Date: Sun, 19 Jan 2025 13:38:44 +0100 Subject: [PATCH 070/101] Update content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Michaël Galien <64089998+Michael-cd30@users.noreply.github.com> Signed-off-by: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> --- .../2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md index 43c3dc2fe6..7e8d7ce121 100644 --- a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md +++ b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md @@ -260,7 +260,7 @@ WHERE AND annee = 2024 ``` -Vous pouvez voir que j'utilise l'opérateur `?`; uniquement valable pour les champs `jsonb` et non ceux en simple `json`. En effet, lorsque l'on requête un champ json/jsonb, un retour vous est fait pour l'ensemble des enregistrements de la table, même ceux ne contenant pas la clé. Comprendre que si votre table comprend 100 000 enregistrements, mais que seuls 100 contiennent la clé "melons", ne pas spécifier cette clause WHERE vous renverrait 100 000 lignes, dont 99 900 de `NULL`.`?` est un opérateur json permettant de poser la question "la clé est-elle présente au premier niveau du champ json pour cet enregistrement ?", et on ne récupérerait que nos 100 enregistrements contenant la clé "melons". +Vous pouvez voir que j'utilise l'opérateur `?`; uniquement valable pour les champs `jsonb` et non ceux en simple `json`. En effet, lorsque l'on requête un champ json/jsonb, un retour vous est fait pour l'ensemble des enregistrements de la table, même ceux ne contenant pas la clé. Comprendre que si votre table contient 100 000 enregistrements, mais que seuls 100 contiennent la clé "melons", ne pas spécifier cette clause WHERE vous renverrait 100 000 lignes, dont 99 900 de `NULL`.`?` est un opérateur json permettant de poser la question "la clé est-elle présente au premier niveau du champ json pour cet enregistrement ?", et on ne récupérerait que nos 100 enregistrements contenant la clé "melons". Je précise que même si vous êtes encore ici, je suppose que vous savez déjà cela. Cependant, la forme `(quelque_chose)::int4` est un raccourci de PostgreSQL pour faire un `cast` soit convertir une valeur dans un autre type. Avec `->>` la valeur nous est renvoyée sous forme de texte et nous la convertissons en entier. From 3665ccfe448de17c6a715372e32af75406f29682 Mon Sep 17 00:00:00 2001 From: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> Date: Sun, 19 Jan 2025 13:44:32 +0100 Subject: [PATCH 071/101] Update content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Michaël Galien <64089998+Michael-cd30@users.noreply.github.com> Signed-off-by: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> --- .../2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md index 7e8d7ce121..c8e1c72f62 100644 --- a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md +++ b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md @@ -293,7 +293,7 @@ Pour récupérer une valeur, on utilise la fonction `jsonb_path_query()` qui pos ```sql SELECT - jsonb_path_query(donnee, '$.cle2[1]') + jsonb_path_query(donnees, '$.cle2[1]') FROM test.test ``` From f8745caf2dc459dd4ec42f100c9780cdde6c36ff Mon Sep 17 00:00:00 2001 From: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> Date: Sun, 19 Jan 2025 13:46:20 +0100 Subject: [PATCH 072/101] Update content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md Signed-off-by: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> --- .../2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md index c8e1c72f62..a35f175bec 100644 --- a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md +++ b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md @@ -262,7 +262,7 @@ WHERE Vous pouvez voir que j'utilise l'opérateur `?`; uniquement valable pour les champs `jsonb` et non ceux en simple `json`. En effet, lorsque l'on requête un champ json/jsonb, un retour vous est fait pour l'ensemble des enregistrements de la table, même ceux ne contenant pas la clé. Comprendre que si votre table contient 100 000 enregistrements, mais que seuls 100 contiennent la clé "melons", ne pas spécifier cette clause WHERE vous renverrait 100 000 lignes, dont 99 900 de `NULL`.`?` est un opérateur json permettant de poser la question "la clé est-elle présente au premier niveau du champ json pour cet enregistrement ?", et on ne récupérerait que nos 100 enregistrements contenant la clé "melons". -Je précise que même si vous êtes encore ici, je suppose que vous savez déjà cela. Cependant, la forme `(quelque_chose)::int4` est un raccourci de PostgreSQL pour faire un `cast` soit convertir une valeur dans un autre type. Avec `->>` la valeur nous est renvoyée sous forme de texte et nous la convertissons en entier. +Je précise que même si vous êtes encore ici, je suppose que vous savez déjà cela. Cependant, la forme `(quelque_chose)::(type)` est un raccourci de PostgreSQL pour faire un `cast` soit convertir une valeur dans un autre type. Avec `->>` la valeur nous est renvoyée sous forme de texte et nous la convertissons en entier. ### Avec du json imbriqué From fcb4aa5a904b8061b17c695fee296dcb4b640b84 Mon Sep 17 00:00:00 2001 From: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> Date: Sun, 19 Jan 2025 13:47:11 +0100 Subject: [PATCH 073/101] Update content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Michaël Galien <64089998+Michael-cd30@users.noreply.github.com> Signed-off-by: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> --- .../2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md index a35f175bec..14f4fa7a51 100644 --- a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md +++ b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md @@ -298,7 +298,7 @@ FROM test.test ``` Le `$` désigne le début du chemin json retourné. Nous faisons suivre ce premier symbole par un point pour passer à l'objet suivant. Puis, par le nom de clé suivant et ainsi de suite jusqu'à la clé recherchée, à laquelle nous collons un `[1]` pour la 2ᵉ valeur de la liste (les valeurs commencent à 0). -Pour plus d'informations sur les `json_path`, vous pouvez consulter la [documentation](https://www.postgresql.org/docs/current/functions-json.html#FUNCTIONS-SQLJSON-PATH) +Pour plus d'informations sur les `json_path`, vous pouvez consulter la [documentation](https://www.postgresql.org/docs/current/functions-json.html#FUNCTIONS-SQLJSON-PATH). ## Attaquons nous au recensement From 264a500d71279fe0a7099f5a575babcf8fccec05 Mon Sep 17 00:00:00 2001 From: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> Date: Sun, 19 Jan 2025 13:49:23 +0100 Subject: [PATCH 074/101] Apply suggestions from code review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Michaël Galien <64089998+Michael-cd30@users.noreply.github.com> Signed-off-by: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> --- .../2025-01-21_travailler-avec-JSON-et-PostgreSQL.md | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md index 14f4fa7a51..400f9d64e4 100644 --- a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md +++ b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md @@ -310,16 +310,11 @@ Les noms de champ commencent tous par P ou C, ceci indique *exploitation princip Vous trouverez [ici](https://github.com/thomas-szczurek/base_donnees_insee/blob/main/sql/import/correction_champs_insee.xlsx) un tableur dont le rôle est de s'occuper de tout ça. -- `STXT` est la fonction qui permet de découper une chaine de texte avec Excel, avec comme arguments `cellule comprenant la chaine initiale` , `position du début de la découpe` , `position de fin de la découpe` -- À partir du fichier récupéré, il vous suffit de copier-coller les noms de champs INSEE sur la première ligne et de récupérer la seconde, qui contiendra les noms de champs formatés. -- On remplace les noms de champ du fichier INSEE original. -- Et on change le nom de cette table temporaire par "rp_population_import.csv" -Avant d'insérer les données dans notre table, nous allons passer par une table temporaire afin de rendre les données accessibles dans Postgres. Utiliser `COPY` de Postgresql serait fastidieux car il faudrait indiquer la centaine de champs que contient le volet population du recensement dans la commande. Et, je n'ai pas honte de dire que j'ai un baobab dans la main à cette idée. Nous sortons donc ce merveilleux logiciel qu'est QGIS. On active les panneaux Explorateur et Explorateur2. On crée une connexion vers la base avec les droits de création, et d'un mouvement gracile du poignet, vous glissez le fichier depuis le panneau Explorateur vers la base Postgres dans l'Explorateur2. Laisser la magie opérer. +Avant d'insérer les données dans notre table, nous allons passer par une table temporaire afin de rendre les données accessibles dans Postgres. Utiliser `COPY` de Postgresql serait fastidieux car il faudrait indiquer la centaine de champs que contient le volet population du recensement dans la commande. Et, je n'ai pas honte de dire que j'ai un baobab dans la main à cette idée. Nous sortons donc ce merveilleux logiciel qu'est QGIS. On active les panneaux Explorateur et Explorateur2. On crée une connexion vers la base avec les droits de création, et d'un mouvement gracile du poignet, vous glissez le fichier depuis le panneau Explorateur vers la base Postgres dans l'Explorateur2. Laissez la magie opérer. -Maintenant, préparez-vous pour peut-être un des INSERT les plus bizarres de votre vie (en tout cas, ça l'a été pour moi !). Arf. Je me rends compte que si je veux bien faire, il faut aussi que j'explique les CTE (c'est très étrange, car encore une fois, si vous êtes encore ici, vous savez très probablement déjà ce qu'est une CTE). +Maintenant, préparez-vous pour peut-être un des INSERT les plus bizarres de votre vie (en tout cas, ça l'a été pour moi !). Arf. Je me rends compte que si je voulais bien faire, il faudrait aussi que j'explique les [CTE](https://www.postgresql.org/docs/current/queries-with.html). Mais pour ne pas trop alourdir, je vous laisser cliquer sur le lien. -CTE veut dire Common Table Expression. C'est une fonctionnalité qui permet grâce à la clause `WITH` d'isoler une sous-requête de la requête principale pour rendre tout un peu plus clair. Elle permet également de la nommer pour pouvoir la réutiliser à plusieurs endroits sans devoir la réécrire. On peut aussi s'en servir pour faire des requêtes récursives avec `WITH RECURSIVE` et si le sujet vous intéresse je vous encourage à aller lire la [magnifique documentation](https://www.postgresql.org/docs/current/queries-with.html#QUERIES-WITH-RECURSIVE) de Postgres a ce sujet. On va utiliser la CTE pour concaténer le nom que l'on veut donner à nos clés avec les valeurs contenues dans notre table temporaire dans une chaine séparée par des `,`. On l'enverra dans une fonction `string_to_array()` puis dans une fonction `jsonb_object()`. On en profitera pour au passage, nettoyer toute tabulation ou retour chariot qui pourrait subsister avec une expression régulière grâce à la fonction `regex_replace()`. (ces caractères se notent `\t`, `\n` et `\r`). Cette dernière fonction prend 3 arguments : la chaine de caractères source, le `pattern` recherché, le texte de remplacement. On y ajoute le *drapeau* optionnel `g` afin de remplacer toutes les occurrences trouvées. @@ -329,7 +324,7 @@ Notez que si votre table temporaire possède un nom différent de "rp_population -- cte concatenant les données avec les clés et nettoyant les caractères spéciaux. WITH d AS ( SELECT - "CODGEO", + CODGEO, regexp_replace('pop_p,' || "POP_P" || ', pop_0_14_ans_p,' || "POP0014_P" || ', pop_15_29_ans_p,' || "POP1529_P" || ', From 0a77c6f667c8d0d7f25c8e549720629ee5bc6da5 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 19 Jan 2025 12:49:53 +0000 Subject: [PATCH 075/101] =?UTF-8?q?[pre-commit.ci]=20Corrections=20automat?= =?UTF-8?q?iques=20appliqu=C3=A9es=20par=20les=20git=20hooks.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md index 400f9d64e4..ad64a95db2 100644 --- a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md +++ b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md @@ -310,12 +310,10 @@ Les noms de champ commencent tous par P ou C, ceci indique *exploitation princip Vous trouverez [ici](https://github.com/thomas-szczurek/base_donnees_insee/blob/main/sql/import/correction_champs_insee.xlsx) un tableur dont le rôle est de s'occuper de tout ça. - Avant d'insérer les données dans notre table, nous allons passer par une table temporaire afin de rendre les données accessibles dans Postgres. Utiliser `COPY` de Postgresql serait fastidieux car il faudrait indiquer la centaine de champs que contient le volet population du recensement dans la commande. Et, je n'ai pas honte de dire que j'ai un baobab dans la main à cette idée. Nous sortons donc ce merveilleux logiciel qu'est QGIS. On active les panneaux Explorateur et Explorateur2. On crée une connexion vers la base avec les droits de création, et d'un mouvement gracile du poignet, vous glissez le fichier depuis le panneau Explorateur vers la base Postgres dans l'Explorateur2. Laissez la magie opérer. Maintenant, préparez-vous pour peut-être un des INSERT les plus bizarres de votre vie (en tout cas, ça l'a été pour moi !). Arf. Je me rends compte que si je voulais bien faire, il faudrait aussi que j'explique les [CTE](https://www.postgresql.org/docs/current/queries-with.html). Mais pour ne pas trop alourdir, je vous laisser cliquer sur le lien. - On va utiliser la CTE pour concaténer le nom que l'on veut donner à nos clés avec les valeurs contenues dans notre table temporaire dans une chaine séparée par des `,`. On l'enverra dans une fonction `string_to_array()` puis dans une fonction `jsonb_object()`. On en profitera pour au passage, nettoyer toute tabulation ou retour chariot qui pourrait subsister avec une expression régulière grâce à la fonction `regex_replace()`. (ces caractères se notent `\t`, `\n` et `\r`). Cette dernière fonction prend 3 arguments : la chaine de caractères source, le `pattern` recherché, le texte de remplacement. On y ajoute le *drapeau* optionnel `g` afin de remplacer toutes les occurrences trouvées. Notez que si votre table temporaire possède un nom différent de "rp_population_import" il vous faudra modifier la clause FROM de la CTE. From 10738874d628222fa763d02b695727873d2e5a4c Mon Sep 17 00:00:00 2001 From: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> Date: Sun, 19 Jan 2025 13:52:33 +0100 Subject: [PATCH 076/101] Update content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md Signed-off-by: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> --- .../2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md index ad64a95db2..c1999f947e 100644 --- a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md +++ b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md @@ -306,7 +306,7 @@ Bien, maintenant que nous avons essayé d'expliquer tant bien que mal les concep Récupérons le dernier millésime du volet population du recensement communal [au format csv sur le site de l'INSEE](https://www.insee.fr/fr/information/8183122) (vous voulez les bases des principaux indicateurs). Pour l'exemple, on utilisera le fichier "Evolution et structure de la population". Il faut tout d'abord nettoyer les noms de champ. En effet, l'INSEE indique le millésime systématiquement dans ses noms de champ, ce qui fait que ces derniers changent tous les ans pour un même indicateur. -Les noms de champ commencent tous par P ou C, ceci indique *exploitation principale* (réponses brutes aux questions du recensement) ou *exploitation complémentaire* (croisement de réponses pour établir un indicateur). Les champs provenant de l'exploitation principale et ceux issus de la complémentaire ne doivent pas être croisés entre eux. Cette information est évidemment à conserver, mais par choix personnel, je préfère la mettre à la fin plutôt qu'au début du nom. +Les noms de champ commencent tous par P ou C, ceci indique *exploitation principale* (réponses brutes aux questions du recensement) ou *exploitation complémentaire* (croisement de réponses pour établir un indicateur). Les champs provenant de l'exploitation principale et ceux issus de la complémentaire ne doivent pas être croisés entre eux. Cette information est évidemment à conserver, mais par choix personnel, je préfère la mettre à la fin plutôt qu'au début du nom. On passera ainsi de champs normés comme ceci : `P18_POP` vers une normalisation de ce type `POP_P`. Vous trouverez [ici](https://github.com/thomas-szczurek/base_donnees_insee/blob/main/sql/import/correction_champs_insee.xlsx) un tableur dont le rôle est de s'occuper de tout ça. From a69f80bda3282dc324d6eb9529c789081d19e2db Mon Sep 17 00:00:00 2001 From: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> Date: Sun, 19 Jan 2025 13:53:05 +0100 Subject: [PATCH 077/101] Update content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Michaël Galien <64089998+Michael-cd30@users.noreply.github.com> Signed-off-by: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> --- .../2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md index c1999f947e..ec98afcd11 100644 --- a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md +++ b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md @@ -304,7 +304,7 @@ Pour plus d'informations sur les `json_path`, vous pouvez consulter la [document Bien, maintenant que nous avons essayé d'expliquer tant bien que mal les concepts avec des exemples simples, car il faut bien commencer quelque part, essayons avec quelque chose d'un peu plus volumineux. -Récupérons le dernier millésime du volet population du recensement communal [au format csv sur le site de l'INSEE](https://www.insee.fr/fr/information/8183122) (vous voulez les bases des principaux indicateurs). Pour l'exemple, on utilisera le fichier "Evolution et structure de la population". Il faut tout d'abord nettoyer les noms de champ. En effet, l'INSEE indique le millésime systématiquement dans ses noms de champ, ce qui fait que ces derniers changent tous les ans pour un même indicateur. +Récupérons le dernier millésime du volet population du recensement communal [au format csv sur le site de l'Insee](https://www.insee.fr/fr/information/8183122) (vous voulez les bases des principaux indicateurs). Pour l'exemple, on utilisera le fichier "Evolution et structure de la population". Il faut tout d'abord nettoyer les noms de champ. En effet, l'Insee indique le millésime systématiquement dans ses noms de champ, ce qui fait que ces derniers changent tous les ans pour un même indicateur. Les noms de champ commencent tous par P ou C, ceci indique *exploitation principale* (réponses brutes aux questions du recensement) ou *exploitation complémentaire* (croisement de réponses pour établir un indicateur). Les champs provenant de l'exploitation principale et ceux issus de la complémentaire ne doivent pas être croisés entre eux. Cette information est évidemment à conserver, mais par choix personnel, je préfère la mettre à la fin plutôt qu'au début du nom. On passera ainsi de champs normés comme ceci : `P18_POP` vers une normalisation de ce type `POP_P`. From 06404752c25a9bdebaf8b297dd27bf576c22df76 Mon Sep 17 00:00:00 2001 From: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> Date: Sun, 19 Jan 2025 13:59:00 +0100 Subject: [PATCH 078/101] Update 2025-01-21_travailler-avec-JSON-et-PostgreSQL.md Signed-off-by: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> --- ...1-21_travailler-avec-JSON-et-PostgreSQL.md | 208 +++++++++--------- 1 file changed, 104 insertions(+), 104 deletions(-) diff --git a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md index ec98afcd11..5061789819 100644 --- a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md +++ b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md @@ -323,110 +323,110 @@ Notez que si votre table temporaire possède un nom différent de "rp_population WITH d AS ( SELECT CODGEO, - regexp_replace('pop_p,' || "POP_P" || ', - pop_0_14_ans_p,' || "POP0014_P" || ', - pop_15_29_ans_p,' || "POP1529_P" || ', - pop_30_44_ans_p,' || "POP3044_P" || ', - pop_45_59_ans_p,' || "POP4559_P" || ', - pop_60_74_ans_p,' || "POP6074_P" || ', - pop_75_89_ans_p,' || "POP7589_P" || ', - pop_90_ans_plus_p,' || "POP90P_P" || ', - hommes_p,' || "POPH_P" || ', - hommes_0_14_ans_p,' || "H0014_P" || ', - hommes_15_29_ans_p,' || "H1529_P" || ', - hommes_30_44_ans_p,' || "H3044_P" || ', - hommes_45_59_ans_p,' || "H4559_P" || ', - hommes_60_74_ans_p,' || "H6074_P" || ', - hommes_75_89_ans_p,' || "H7589_P" || ', - hommes_90_ans_plus_p,' || "H90P_P" || ', - hommes_0_19_ans_p,' || "H0019_P" || ', - hommes_20_64_ans_p,' || "H2064_P" || ', - hommes_65_ans_plus_p,' || "H65P_P" || ', - femmes_p,' || "POPF_P" || ', - femmes_0_14_ans_p,' || "F0014_P" || ', - femmes_15_29_ans_p,' || "F1529_P" || ', - femmes_30_44_ans_p,' || "F3044_P" || ', - femmes_45_59_ans_p,' || "F4559_P" || ', - femmes_60_74_ans_p,' || "F6074_P" || ', - femmes_75_89_ans_p,' || "F7589_P" || ', - femmes_90_ans_plus_p,' || "F90P_P" || ', - femmes_0_19_ans_p,' || "F0019_P" || ', - femmes_20_64_ans_p,' || "F2064_P" || ', - femmes_65_ans_plus_p,' || "F65P_P" || ', - pop_1an_ou_plus_localisee_1an_auparavant_p,' || "POP01P_P" || ', - pop_1an_ou_plus_meme_logement_1an_auparavant_p,' || "POP01P_IRAN1_P" || ', - pop_1an_ou_plus_meme_commune_1an_auparavant_p,' || "POP01P_IRAN2_P" || ', - pop_1an_ou_plus_meme_departement_1an_auparavant_p,' || "POP01P_IRAN3_P" || ', - pop_1an_ou_plus_meme_region_1an_auparavant_p,' || "POP01P_IRAN4_P" || ', - pop_1an_ou_plus_autre_region_1an_auparavant_p,' || "POP01P_IRAN5_P" || ', - pop_1an_ou_plus_un_dom_1an_auparavant_p,' || "POP01P_IRAN6_P" || ', - pop_1an_ou_plus_hors_metropole_ou_dom_1an_auparavant_p,' || "POP01P_IRAN7_P" || ', - pop_1_14ans_autre_logement_1an_auparavant_p,' || "POP0114_IRAN2P_P" || ', - pop_1_14ans_meme_commune_1an_auparavant_p,' || "POP0114_IRAN2_P" || ', - pop_1_14ans_autre_commune_1an_auparavant_p,' || "POP0114_IRAN3P_P" || ', - pop_15_24ans_autre_logement_1an_auparavant_p,' || "POP1524_IRAN2P_P" || ', - pop_15_24ans_meme_commune_1an_auparavant_p,' || "POP1524_IRAN2_P" || ', - pop_15_24ans_autre_commune_1an_auparavant_p,' || "POP1524_IRAN3P_P" || ', - pop_25_54ans_autre_logement_1an_auparavant_p,' || "POP2554_IRAN2P_P" || ', - pop_25_54ans_meme_commune_1an_auparavant_p,' || "POP2554_IRAN2_P" || ', - pop_25_54ans_autre_commune_1an_auparavant_p,' || "POP2554_IRAN3P_P" || ', - pop_55_ou_plus_autre_logement_1an_auparavant_p,' || "POP55P_IRAN2P_P" || ', - pop_55_ou_plus_meme_commune_1an_auparavant_p,' || "POP55P_IRAN2_P" || ', - pop_55_ou_plus_autre_commune_1an_auparavant_p,' || "POP55P_IRAN3P_P" || ', - pop_15_ans_plus_c,' || "POP15P_C" || ', - agriculteurs_15_ans_plus_c,' || "POP15P_CS1_C" || ', - artisants_commercants_chefs_entreprise_15_ans_plus_c,' || "POP15P_CS2_C" || ', - cadres_prof_intel_sup_15_ans_plus_c,' || "POP15P_CS3_C" || ', - professions_intermediaires_15_ans_plus_c,' || "POP15P_CS4_C" || ', - employes_15_ans_plus_c,' || "POP15P_CS5_C" || ', - ouvriers_15_ans_plus_c,' || "POP15P_CS6_C" || ', - retraites_15_ans_plus_c,' || "POP15P_CS7_C" || ', - autres_15_ans_plus_c,' || "POP15P_CS8_C" || ', - hommes_15_ans_plus_c,' || "H15P_C" || ', - h_agriculteurs_15_ans_plus_c,' || "H15P_CS1_C" || ', - h_artisants_commercants_chefs_entreprise_15_ans_plus_c,' || "H15P_CS2_C" || ', - h_cadres_prof_intel_sup_15_ans_plus_c,' || "H15P_CS3_C" || ', - h_professions_intermediaires_15_ans_plus_c,' || "H15P_CS4_C" || ', - h_employes_15_ans_plus_c,' || "H15P_CS5_C" || ', - h_ouvriers_15_ans_plus_c,' || "H15P_CS6_C" || ', - h_retraites_15_ans_plus_c,' || "H15P_CS7_C" || ', - h_autres_15_ans_plus_c,' || "H15P_CS8_C" || ', - femmes_15_ans_plus_c,' || "F15P_C" || ', - f_agricultrices_15_ans_plus_c,' || "F15P_CS1_C" || ', - f_artisanes_commercantes_cheffes_entreprise_15_ans_plus_c,' || "F15P_CS2_C" || ', - f_cadres_prof_intel_sup_15_ans_plus_c,' || "F15P_CS3_C" || ', - f_professions_intermediaires_15_ans_plus_c,' || "F15P_CS4_C" || ', - f_employees_15_ans_plus_c,' || "F15P_CS5_C" || ', - f_ouvrieres_15_ans_plus_c,' || "F15P_CS6_C" || ', - f_retraitees_15_ans_plus_c,' || "F15P_CS7_C" || ', - f_autres_15_ans_plus_c,' || "F15P_CS8_C" || ', - population_15_24_ans_c,' || "POP1524_C" || ', - pop_15_24_ans_agriculteurs_c,' || "POP1524_CS1_C" || ', - pop_15_24_ans_artisants_commercants_chefs_entreprise_c,' || "POP1524_CS2_C" || ', - pop_15_24_ans_cadres_prof_intel_sup_c,' || "POP1524_CS3_C" || ', - pop_15_24_ans_professions_intermediaires_c,' || "POP1524_CS4_C" || ', - pop_15_24_ans_employes_c,' || "POP1524_CS5_C" || ', - pop_15_24_ans_ouvriers_c,' || "POP1524_CS6_C" || ', - pop_15_24_ans_retraites_c,' || "POP1524_CS7_C" || ', - pop_15_24_ans_autres_c,' || "POP1524_CS8_C" || ', - population_25_54_ans_c,' || "POP2554_C" || ', - pop_25_54_ans_agriculteurs_c,' || "POP2554_CS1_C" || ', - pop_25_54_ans_artisants_commercants_chefs_entreprise_c,' || "POP2554_CS2_C" || ', - pop_25_54_ans_cadres_prof_intel_sup_c,' || "POP2554_CS3_C" || ', - pop_25_54_ans_professions_intermediaires_c,' || "POP2554_CS4_C" || ', - pop_25_54_ans_employes_c,' || "POP2554_CS5_C" || ', - pop_25_54_ans_ouvriers_c,' || "POP2554_CS6_C" || ', - pop_25_54_ans_retraites_c,' || "POP2554_CS7_C" || ', - pop_25_54_ans_autres_c,' || "POP2554_CS8_C" || ', - population_55_ans_et_plus_c,' || "POP55P_C" || ', - pop_55_ans_et_plus_ans_agriculteurs_c,' || "POP55P_CS1_C" || ', - pop_55_ans_et_plus_ans_artisants_commercants_chefs_entreprise_c,' || "POP55P_CS2_C" || ', - pop_55_ans_et_plus_ans_cadres_prof_intel_sup_c,' || "POP55P_CS3_C" || ', - pop_55_ans_et_plus_ans_professions_intermediaires_c,' || "POP55P_CS4_C" || ', - pop_55_ans_et_plus_ans_employes_c,' || "POP55P_CS5_C" || ', - pop_55_ans_et_plus_ans_ouvriers_c,' || "POP55P_CS6_C" || ', - pop_55_ans_et_plus_ans_retraites_c,' || "POP55P_CS7_C" || ', - pop_55_ans_et_plus_ans_autres_c,' || "POP55P_CS8_C", + regexp_replace('pop_p,' || POP_P || ', + pop_0_14_ans_p,' || POP0014_P || ', + pop_15_29_ans_p,' || POP1529_P || ', + pop_30_44_ans_p,' || POP3044_P || ', + pop_45_59_ans_p,' || POP4559_P || ', + pop_60_74_ans_p,' || POP6074_P || ', + pop_75_89_ans_p,' || POP7589_P || ', + pop_90_ans_plus_p,' || POP90P_P || ', + hommes_p,' || POPH_P || ', + hommes_0_14_ans_p,' || H0014_P || ', + hommes_15_29_ans_p,' || H1529_P || ', + hommes_30_44_ans_p,' || H3044_P || ', + hommes_45_59_ans_p,' || H4559_P || ', + hommes_60_74_ans_p,' || H6074_P || ', + hommes_75_89_ans_p,' || H7589_P || ', + hommes_90_ans_plus_p,' || H90P_P || ', + hommes_0_19_ans_p,' || H0019_P || ', + hommes_20_64_ans_p,' || H2064_P || ', + hommes_65_ans_plus_p,' || H65P_P || ', + femmes_p,' || POPF_P || ', + femmes_0_14_ans_p,' || F0014_P || ', + femmes_15_29_ans_p,' || F1529_P || ', + femmes_30_44_ans_p,' || F3044_P || ', + femmes_45_59_ans_p,' || F4559_P || ', + femmes_60_74_ans_p,' || F6074_P || ', + femmes_75_89_ans_p,' || F7589_P || ', + femmes_90_ans_plus_p,' || F90P_P || ', + femmes_0_19_ans_p,' || F0019_P || ', + femmes_20_64_ans_p,' || F2064_P || ', + femmes_65_ans_plus_p,' || F65P_P || ', + pop_1an_ou_plus_localisee_1an_auparavant_p,' || POP01P_P || ', + pop_1an_ou_plus_meme_logement_1an_auparavant_p,' || POP01P_IRAN1_P || ', + pop_1an_ou_plus_meme_commune_1an_auparavant_p,' || POP01P_IRAN2_P || ', + pop_1an_ou_plus_meme_departement_1an_auparavant_p,' || POP01P_IRAN3_P || ', + pop_1an_ou_plus_meme_region_1an_auparavant_p,' || POP01P_IRAN4_P || ', + pop_1an_ou_plus_autre_region_1an_auparavant_p,' || POP01P_IRAN5_P || ', + pop_1an_ou_plus_un_dom_1an_auparavant_p,' || POP01P_IRAN6_P || ', + pop_1an_ou_plus_hors_metropole_ou_dom_1an_auparavant_p,' || POP01P_IRAN7_P || ', + pop_1_14ans_autre_logement_1an_auparavant_p,' || POP0114_IRAN2P_P || ', + pop_1_14ans_meme_commune_1an_auparavant_p,' || POP0114_IRAN2_P || ', + pop_1_14ans_autre_commune_1an_auparavant_p,' || POP0114_IRAN3P_P || ', + pop_15_24ans_autre_logement_1an_auparavant_p,' || POP1524_IRAN2P_P || ', + pop_15_24ans_meme_commune_1an_auparavant_p,' || POP1524_IRAN2_P || ', + pop_15_24ans_autre_commune_1an_auparavant_p,' || POP1524_IRAN3P_P || ', + pop_25_54ans_autre_logement_1an_auparavant_p,' || POP2554_IRAN2P_P || ', + pop_25_54ans_meme_commune_1an_auparavant_p,' || POP2554_IRAN2_P || ', + pop_25_54ans_autre_commune_1an_auparavant_p,' || POP2554_IRAN3P_P || ', + pop_55_ou_plus_autre_logement_1an_auparavant_p,' || POP55P_IRAN2P_P || ', + pop_55_ou_plus_meme_commune_1an_auparavant_p,' || POP55P_IRAN2_P || ', + pop_55_ou_plus_autre_commune_1an_auparavant_p,' || POP55P_IRAN3P_P || ', + pop_15_ans_plus_c,' || POP15P_C || ', + agriculteurs_15_ans_plus_c,' || POP15P_CS1_C || ', + artisants_commercants_chefs_entreprise_15_ans_plus_c,' || POP15P_CS2_C || ', + cadres_prof_intel_sup_15_ans_plus_c,' || POP15P_CS3_C || ', + professions_intermediaires_15_ans_plus_c,' || POP15P_CS4_C || ', + employes_15_ans_plus_c,' || POP15P_CS5_C || ', + ouvriers_15_ans_plus_c,' || POP15P_CS6_C || ', + retraites_15_ans_plus_c,' || POP15P_CS7_C || ', + autres_15_ans_plus_c,' || POP15P_CS8_C || ', + hommes_15_ans_plus_c,' || H15P_C || ', + h_agriculteurs_15_ans_plus_c,' || H15P_CS1_C || ', + h_artisants_commercants_chefs_entreprise_15_ans_plus_c,' || H15P_CS2_C || ', + h_cadres_prof_intel_sup_15_ans_plus_c,' || H15P_CS3_C || ', + h_professions_intermediaires_15_ans_plus_c,' || H15P_CS4_C || ', + h_employes_15_ans_plus_c,' || H15P_CS5_C || ', + h_ouvriers_15_ans_plus_c,' || H15P_CS6_C || ', + h_retraites_15_ans_plus_c,' || H15P_CS7_C || ', + h_autres_15_ans_plus_c,' || H15P_CS8_C || ', + femmes_15_ans_plus_c,' || F15P_C || ', + f_agricultrices_15_ans_plus_c,' || F15P_CS1_C || ', + f_artisanes_commercantes_cheffes_entreprise_15_ans_plus_c,' || F15P_CS2_C || ', + f_cadres_prof_intel_sup_15_ans_plus_c,' || F15P_CS3_C || ', + f_professions_intermediaires_15_ans_plus_c,' || F15P_CS4_C || ', + f_employees_15_ans_plus_c,' || F15P_CS5_C || ', + f_ouvrieres_15_ans_plus_c,' || F15P_CS6_C || ', + f_retraitees_15_ans_plus_c,' || F15P_CS7_C || ', + f_autres_15_ans_plus_c,' || F15P_CS8_C || ', + population_15_24_ans_c,' || POP1524_C || ', + pop_15_24_ans_agriculteurs_c,' || POP1524_CS1_C || ', + pop_15_24_ans_artisants_commercants_chefs_entreprise_c,' || POP1524_CS2_C || ', + pop_15_24_ans_cadres_prof_intel_sup_c,' || POP1524_CS3_C || ', + pop_15_24_ans_professions_intermediaires_c,' || POP1524_CS4_C || ', + pop_15_24_ans_employes_c,' || POP1524_CS5_C || ', + pop_15_24_ans_ouvriers_c,' || POP1524_CS6_C || ', + pop_15_24_ans_retraites_c,' || POP1524_CS7_C || ', + pop_15_24_ans_autres_c,' || POP1524_CS8_C || ', + population_25_54_ans_c,' || POP2554_C || ', + pop_25_54_ans_agriculteurs_c,' || POP2554_CS1_C || ', + pop_25_54_ans_artisants_commercants_chefs_entreprise_c,' || POP2554_CS2_C || ', + pop_25_54_ans_cadres_prof_intel_sup_c,' || POP2554_CS3_C || ', + pop_25_54_ans_professions_intermediaires_c,' || POP2554_CS4_C || ', + pop_25_54_ans_employes_c,' || POP2554_CS5_C || ', + pop_25_54_ans_ouvriers_c,' || POP2554_CS6_C || ', + pop_25_54_ans_retraites_c,' || POP2554_CS7_C || ', + pop_25_54_ans_autres_c,' || POP554_CS8_C || ', + population_55_ans_et_plus_c,' || POP55P_C || ', + pop_55_ans_et_plus_ans_agriculteurs_c,' || POP55P_CS1_C || ', + pop_55_ans_et_plus_ans_artisants_commercants_chefs_entreprise_c,' || POP55P_CS2_C || ', + pop_55_ans_et_plus_ans_cadres_prof_intel_sup_c,' || POP55P_CS3_C || ', + pop_55_ans_et_plus_ans_professions_intermediaires_c,' || POP55P_CS4_C || ', + pop_55_ans_et_plus_ans_employes_c,' || POP55P_CS5_C || ', + pop_55_ans_et_plus_ans_ouvriers_c,' || POP55P_CS6_C || ', + pop_55_ans_et_plus_ans_retraites_c,' || POP55P_CS7_C || ', + pop_55_ans_et_plus_ans_autres_c,' || POP55P_CS8_C, E'[\t\n\r]','','g') AS data FROM insee.rp_population_import ) From 0b9da7290e0d11fa40d8389194888c06e3296cad Mon Sep 17 00:00:00 2001 From: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> Date: Mon, 20 Jan 2025 11:06:54 +0100 Subject: [PATCH 079/101] Apply suggestions from code review Co-authored-by: Julien Signed-off-by: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> --- ...1-21_travailler-avec-JSON-et-PostgreSQL.md | 20 ++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md index 5061789819..47d3c2d1ec 100644 --- a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md +++ b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md @@ -21,32 +21,38 @@ tags: :calendar: Date de publication initiale : {{ page.meta.date | date_localized }} + +![logo JSON](https://cdn.geotribu.fr/img/logos-icones/programmation/json.png){: .img-thumbnail-left } + Dans le cadre d'un projet personnel, j'ai voulu stocker une bonne partie des données du recensement de l'Insee dans une base PostgreSQL avec des tables multimillésimes. Problème, au sein d'un même jeu de données, les champs peuvent changer au cours des années et cela empêche de pouvoir dégager une structure de table fixe, ce qui est assez gênant vous en conviendrez. La solution ? Passer par des données semi-structurées, soit stocker ces données en JSON dans le champ d'une table. Cet article se veut un condensé de cette expérience. -!!! warning +!!! info "Obsolescence non programmée" Ces travaux ont été réalisés avant la sortie de PostgreSQL 17 qui ajoute d'importantes fonctionnalités pour le JSON avec les [`JSON_TABLE`](https://doc.postgresql.fr/17/functions-json.html#FUNCTIONS-SQLJSON-TABLE), elles ne seront pas évoquées ici. -Puisque nous allons parler de json et de données semi-structurées, je me sens dans l'obligation de commencer cet article par un avertissement. +Puisque nous allons parler de JSON et de données semi-structurées, je me sens dans l'obligation de commencer cet article par un avertissement. **Le modèle relationnel, c'est bon, mangez-en, et les contraintes d'intégrités ont été inventées pour de bonnes raisons.** -Cet article ne se veut surtout pas être une invitation à partir en mode yolo sur la gestion des données "c'est bon ya qu'a tout mettre en json" (comme un vulgaire dev qui mettrait tout dans MongoDB diraient les mauvaises langues). +Cet article ne se veut surtout pas être une invitation à partir en mode YOLO sur la gestion des données "c'est bon ya qu'a tout mettre en JSON" (comme un vulgaire dev qui mettrait tout dans MongoDB diraient les mauvaises langues). -## Le JSON pour les débutant.es +## Le JSON pour les débutant⸱es -Pour celles et ceux qui ne connaissent pas le `json`, il s'agit d'un format textuel de représentation des données venant du Java Script fonctionnant en partie sur un système de `clé : valeur` qu'on peut voir comme une sorte d'évolution du `xml`. +Pour celles et ceux qui ne connaissent pas le [JSON](https://www.json.org/json-fr.html) il s'agit d'un format textuel de représentation des données venant du JavaScript fonctionnant en partie sur un système de `clé : valeur` qu'on peut voir comme une sorte d'évolution du XML. ```json {"clé_1": "valeur", "clé_2": "valeur", "clé_3": "valeur"} ``` -pas besoin de guillemets pour les nombres +Pas besoin de guillemets pour les nombres : ```json {"nb_champignons": 42, "nb_tomates": 31, "prenom": "roger"} ``` -Les valeurs peuvent prendre deux formes. Soit une valeur unique comme dans l'exemple ci-dessus, soit un `array`, une liste, qu'on place entre `[]`, les deux pouvant êtres combinés au sein d'un seul objet json. +Les valeurs peuvent prendre deux formes : + +- soit une valeur unique comme dans l'exemple ci-dessus, +- soit un `array`, une liste, qu'on place entre `[]`, les deux pouvant êtres combinés au sein d'un seul objet JSON. ```json {"prenoms": ["elodie", "roger", "fatima"], "nb_champgnons": 42} From 85df36ad19813540102c7f79a42825c95d52cf05 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 20 Jan 2025 10:07:40 +0000 Subject: [PATCH 080/101] =?UTF-8?q?[pre-commit.ci]=20Corrections=20automat?= =?UTF-8?q?iques=20appliqu=C3=A9es=20par=20les=20git=20hooks.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md | 1 - 1 file changed, 1 deletion(-) diff --git a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md index 47d3c2d1ec..058bc33cde 100644 --- a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md +++ b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md @@ -21,7 +21,6 @@ tags: :calendar: Date de publication initiale : {{ page.meta.date | date_localized }} - ![logo JSON](https://cdn.geotribu.fr/img/logos-icones/programmation/json.png){: .img-thumbnail-left } Dans le cadre d'un projet personnel, j'ai voulu stocker une bonne partie des données du recensement de l'Insee dans une base PostgreSQL avec des tables multimillésimes. Problème, au sein d'un même jeu de données, les champs peuvent changer au cours des années et cela empêche de pouvoir dégager une structure de table fixe, ce qui est assez gênant vous en conviendrez. La solution ? Passer par des données semi-structurées, soit stocker ces données en JSON dans le champ d'une table. Cet article se veut un condensé de cette expérience. From 27347566733cd293db85f402813961b2d597f006 Mon Sep 17 00:00:00 2001 From: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> Date: Mon, 20 Jan 2025 11:21:10 +0100 Subject: [PATCH 081/101] Update content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md Co-authored-by: Julien Signed-off-by: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> --- .../2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md index 058bc33cde..3e0a41a88a 100644 --- a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md +++ b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md @@ -59,7 +59,7 @@ Les valeurs peuvent prendre deux formes : Ce qu'on appelle un objet, c'est tout ce qu'il se trouve entre les `{}` qui servent à déclarer le dit objet. Pour complexifier tout ça, on peut imbriquer les objets et ainsi vous donner un exemple un peu plus parlant que de parler de tomates et de champignons : -```json +```json title="Exemple de JSON des superhéros" linenums="1" { "squadName": "Super hero squad", "homeTown": "Metro City", From f2c4601bdd7ed213246de70d488b5374317446c8 Mon Sep 17 00:00:00 2001 From: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> Date: Mon, 20 Jan 2025 11:21:35 +0100 Subject: [PATCH 082/101] Apply suggestions from code review Signed-off-by: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> --- .../2025-01-21_travailler-avec-JSON-et-PostgreSQL.md | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md index 3e0a41a88a..0956d51576 100644 --- a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md +++ b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md @@ -139,6 +139,8 @@ En voici cependant un schéma succinct pour aider à la compréhension du reste ![modele_de_donnees](https://cdn.geotribu.fr/img/articles-blog-rdp/articles/2024/postgresql_json/modele.png){: .img-center loading=lazy } +*Ne vous préoccupez pas des tables empilées tout à droite, ce sont des partitions de la table `donnees_communes`. Le sujet ne sera pas évoqué ici et elles ne sont nécessaires pour l'exemple.* + Partant d'un schéma nommé `insee`, on va créer deux tables. La première contiendra la liste des *bases* disponibles, les différents volets du recensement. Une seconde permettant de stocker les données ; pour rester concentré sur le json, on va s'épargner 95% du modèle sous-jacent. On ne gérera donc pas ici les codes communes, etc. En bonus pour celles et ceux voulant tester ce que Postgres a dans le ventre, je proposerai une variante juste en dessous : ```sql @@ -182,14 +184,7 @@ INSERT INTO insee.bases (nom) VALUES ('rp_emploi') ``` -### (Bonus) Partition de la table données - -Imaginons que nous travaillons sur l'ensemble des données du recensement (soit 6 fichiers sources), de 2015 à 2021 pour environ 35 000 communes. On va arriver sur du 1,5 million d'enregistrements. Entendons-nous bien, à l'échelle de Postgres ça ne reste pas grand-chose. Mais, si comme moi, vous voulez voir ce qu'on peut tirer des entrailles de Postres, ça permet de pouvoir commencer à justifier d'utiliser certaines fonctionnalités avancées. Une table partitionnée, c'est quoi ? -- C'est une table découpée en plusieurs morceaux où une table *parent* contrôlera les tables *enfants*. -- Une table partitionnée peut se découper selon la valeur d'un champ ou une période temporelle ("fais -moi une partition tous les mois") -- On peut requêter la table parent ou les tables enfants. -- Une table doit être partitionnée à sa création, une table déjà existante ne peut pas être convertie. De nouvelles partitions peuvent toutefois être créées à volonté. - Cela peut être intéressant pour limiter la taille des scans séquentiels qui se feront sur des tables plus petites que la table *parent*, où pour se faciliter la gestion de tables volumineuses. - On peut attacher / détacher une partition (qui devient alors une table classique) avec les "mots" `ATTACH PARTITION` / `DETACH PARTITION` y compris sur une base en production grâce à l'option `CONCURENTLY` - Une clé primaire de table partitionnée **doit** être composite, c'est-à-dire que son unicité sera vérifiée par la composition de plusieurs champs et contenir obligatoirement le champ de partitionnement. From 817dcc96daa51223afcda21097b15dbcb4ed8ded Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 20 Jan 2025 10:22:05 +0000 Subject: [PATCH 083/101] =?UTF-8?q?[pre-commit.ci]=20Corrections=20automat?= =?UTF-8?q?iques=20appliqu=C3=A9es=20par=20les=20git=20hooks.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md | 1 - 1 file changed, 1 deletion(-) diff --git a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md index 0956d51576..69548942b7 100644 --- a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md +++ b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md @@ -184,7 +184,6 @@ INSERT INTO insee.bases (nom) VALUES ('rp_emploi') ``` - - Cela peut être intéressant pour limiter la taille des scans séquentiels qui se feront sur des tables plus petites que la table *parent*, où pour se faciliter la gestion de tables volumineuses. - On peut attacher / détacher une partition (qui devient alors une table classique) avec les "mots" `ATTACH PARTITION` / `DETACH PARTITION` y compris sur une base en production grâce à l'option `CONCURENTLY` - Une clé primaire de table partitionnée **doit** être composite, c'est-à-dire que son unicité sera vérifiée par la composition de plusieurs champs et contenir obligatoirement le champ de partitionnement. From 066163c8274429e268556ef6b83ce80729296328 Mon Sep 17 00:00:00 2001 From: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> Date: Mon, 20 Jan 2025 11:23:32 +0100 Subject: [PATCH 084/101] Update content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md Signed-off-by: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> --- .../2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md index 69548942b7..43d3a21484 100644 --- a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md +++ b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md @@ -476,7 +476,10 @@ SELECT a.premiere AS premiere_annee_presence, a.derniere AS derniere_annee_presence FROM insee.correspondance_clefs_champs AS c, - LATERAL (SELECT min(annee) AS premiere FROM insee.donnees_communes WHERE fk_base = c.fk_base AND donnees ? c.clef_json) AS a + LATERAL (SELECT + min(annee) AS premiere, + max(annee) AS derniere + FROM insee.donnees_communes WHERE fk_base = c.fk_base AND donnees ? c.clef_json) AS a ORDER BY fk_base, clef_json; ``` From 7e84e8d14d115d491ce6a9359f2866c54ca3f7b2 Mon Sep 17 00:00:00 2001 From: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> Date: Mon, 20 Jan 2025 11:23:49 +0100 Subject: [PATCH 085/101] Update content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md Co-authored-by: Julien Signed-off-by: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> --- .../2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md index 43d3a21484..39811597f2 100644 --- a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md +++ b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md @@ -115,6 +115,8 @@ PostgreSQL est capable de stocker les données/objets au format json dans des ch ## Les index +![icône index](https://cdn.geotribu.fr/img/logos-icones/divers/index_pointeur.webp){: .img-thumbnail-left } + Il est possible d'indexer un champ de type `json` / `jsonb` sur ses clés **de premier niveau** et cela se fait avec des index de type `GIN` : ```sql From 662171da5ea7770f740eb599e3f1dd024cc77da9 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 20 Jan 2025 10:24:00 +0000 Subject: [PATCH 086/101] =?UTF-8?q?[pre-commit.ci]=20Corrections=20automat?= =?UTF-8?q?iques=20appliqu=C3=A9es=20par=20les=20git=20hooks.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md index 39811597f2..4a18dc44e0 100644 --- a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md +++ b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md @@ -480,7 +480,7 @@ SELECT FROM insee.correspondance_clefs_champs AS c, LATERAL (SELECT min(annee) AS premiere, - max(annee) AS derniere + max(annee) AS derniere FROM insee.donnees_communes WHERE fk_base = c.fk_base AND donnees ? c.clef_json) AS a ORDER BY fk_base, clef_json; ``` From 180ead09345f6e797b059da995bb8dcb06a99fe0 Mon Sep 17 00:00:00 2001 From: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> Date: Mon, 20 Jan 2025 11:26:39 +0100 Subject: [PATCH 087/101] Update content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md Co-authored-by: Julien Signed-off-by: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> --- .../2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md index 4a18dc44e0..264b0be181 100644 --- a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md +++ b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md @@ -131,7 +131,7 @@ CREATE INDEX idx_tb_champjson ON tb USING gin (champ_json -> cle_ou_se_situent_l On expliquera ce `->` par la suite. -Pour les aventuriers et aventurières, il existe une extention de PostgreSQL `btree_gin` permettant de faire des index multi-champs mixant des champs classiques et json. Elle est disponible nativement à l'installation de PostgreSQL et ne vous demandera pas de devenir développeur.se C/C++ pour l'installer (coucou les fdw parquet ! Ca va par chez vous ?). +Pour les aventuriers et aventurières, il existe une extension de PostgreSQL `btree_gin` permettant de faire des index multi-champs mixant des champs classiques et `json`. Elle est disponible nativement à l'installation de PostgreSQL et ne vous demandera pas de devenir développeur⸱se C/C++ pour l'installer (coucou les FDW parquet ! Ça va par chez vous ?). ## Création des tables From c9d10d1461cf0208d5234a5ed40c6ddf51e7f83c Mon Sep 17 00:00:00 2001 From: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> Date: Mon, 20 Jan 2025 11:27:37 +0100 Subject: [PATCH 088/101] Apply suggestions from code review Co-authored-by: Julien Signed-off-by: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> --- ...025-01-21_travailler-avec-JSON-et-PostgreSQL.md | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md index 264b0be181..98ee97acee 100644 --- a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md +++ b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md @@ -135,7 +135,7 @@ Pour les aventuriers et aventurières, il existe une extension de PostgreSQL `bt ## Création des tables -Je vais éviter de vous spammer du `DDL` ([Data Definition Language](https://fr.wikipedia.org/wiki/Langage_de_d%C3%A9finition_de_donn%C3%A9es), mais vous pourrez retrouver le schéma complet de la base [ici](https://github.com/thomas-szczurek/base_donnees_insee/tree/main/sql/creation_tables) +Je vais éviter de vous spammer du `DDL` ([Data Definition Language](https://fr.wikipedia.org/wiki/Langage_de_d%C3%A9finition_de_donn%C3%A9es), mais vous pourrez retrouver le schéma complet de la base [ici](https://github.com/thomas-szczurek/base_donnees_insee/tree/main/sql/creation_tables). En voici cependant un schéma succinct pour aider à la compréhension du reste de l'article : @@ -186,19 +186,8 @@ INSERT INTO insee.bases (nom) VALUES ('rp_emploi') ``` -- Cela peut être intéressant pour limiter la taille des scans séquentiels qui se feront sur des tables plus petites que la table *parent*, où pour se faciliter la gestion de tables volumineuses. -- On peut attacher / détacher une partition (qui devient alors une table classique) avec les "mots" `ATTACH PARTITION` / `DETACH PARTITION` y compris sur une base en production grâce à l'option `CONCURENTLY` -- Une clé primaire de table partitionnée **doit** être composite, c'est-à-dire que son unicité sera vérifiée par la composition de plusieurs champs et contenir obligatoirement le champ de partitionnement. - -Vous pouvez aussi lire le contenu de la [documentation à ce sujet](https://doc.postgresql.fr/17/ddl-partitioning.html) - -À quoi ressemblerait la création de insee.donnees_communes si on la partitionnait selon les différentes sources de données ? ```sql -BEGIN; -CREATE TABLE insee.donnees_communes ( - pk_id int4 GENERATED BY DEFAULT AS IDENTITY, - code_commune text NOT NULL CHECK length(code_commune) = 5, annee int2 NOT NULL, fk_base int2 NOT NULL, donnees jsonb NOT NULL, @@ -215,7 +204,6 @@ CREATE TABLE insee.donnees_communes_fk_6 PARTITION OF insee.donnees_communes FOR END; ``` -On remarque quelques changements : la déclaration de la clé primaire composite dans une `CONSTRAINT`, le `PARTITION BY LIST` qu'on fait suivre par le champ de partitionnement, et la création des différentes partitions suivant les valeurs de ce champ. ## Insertion de données et récupération From 6fa8b323c8ae668849bd0aa834e5c89ba734e162 Mon Sep 17 00:00:00 2001 From: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> Date: Mon, 20 Jan 2025 11:27:50 +0100 Subject: [PATCH 089/101] Update content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md Co-authored-by: Julien Signed-off-by: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> --- .../2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md index 98ee97acee..856e5c5538 100644 --- a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md +++ b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md @@ -139,7 +139,7 @@ Je vais éviter de vous spammer du `DDL` ([Data Definition Language](https://fr. En voici cependant un schéma succinct pour aider à la compréhension du reste de l'article : -![modele_de_donnees](https://cdn.geotribu.fr/img/articles-blog-rdp/articles/2024/postgresql_json/modele.png){: .img-center loading=lazy } +![Diagramme du modèle de la base de données](https://cdn.geotribu.fr/img/articles-blog-rdp/articles/2024/postgresql_json/modele.png){: .img-center loading=lazy } *Ne vous préoccupez pas des tables empilées tout à droite, ce sont des partitions de la table `donnees_communes`. Le sujet ne sera pas évoqué ici et elles ne sont nécessaires pour l'exemple.* From 6a90f81fcd931878259ec86f9a72502cb1632eee Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 20 Jan 2025 10:28:06 +0000 Subject: [PATCH 090/101] =?UTF-8?q?[pre-commit.ci]=20Corrections=20automat?= =?UTF-8?q?iques=20appliqu=C3=A9es=20par=20les=20git=20hooks.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md index 856e5c5538..25ee5464d7 100644 --- a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md +++ b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md @@ -186,7 +186,6 @@ INSERT INTO insee.bases (nom) VALUES ('rp_emploi') ``` - ```sql annee int2 NOT NULL, fk_base int2 NOT NULL, @@ -204,7 +203,6 @@ CREATE TABLE insee.donnees_communes_fk_6 PARTITION OF insee.donnees_communes FOR END; ``` - ## Insertion de données et récupération Pour passer des données textuelles / SQL vers des données encodées en JSON dans le champ dédié qui va bien, PostgreSQL dispose de [*quelques* fonctions](https://docs.postgresql.fr/17/functions-json.html#FUNCTIONS-JSON-PROCESSING). Nous allons utiliser la fonction `jsonb_object()` qui permet de transformer un `array` sql sous forme `clef1, valeur1, clef2, valeur2 ....` en objet `jsonb` qui n'aura qu'un niveau d'imbrication. D'autres fonctions sont disponibles pour des objets plus complexes (comme `jsonb_build_object()`). From 91357d284d120245583d235be144e5d252d49349 Mon Sep 17 00:00:00 2001 From: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> Date: Mon, 20 Jan 2025 11:29:06 +0100 Subject: [PATCH 091/101] Update content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md Co-authored-by: Julien Signed-off-by: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> --- .../2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md index 25ee5464d7..e4406ccfce 100644 --- a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md +++ b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md @@ -143,7 +143,11 @@ En voici cependant un schéma succinct pour aider à la compréhension du reste *Ne vous préoccupez pas des tables empilées tout à droite, ce sont des partitions de la table `donnees_communes`. Le sujet ne sera pas évoqué ici et elles ne sont nécessaires pour l'exemple.* -Partant d'un schéma nommé `insee`, on va créer deux tables. La première contiendra la liste des *bases* disponibles, les différents volets du recensement. Une seconde permettant de stocker les données ; pour rester concentré sur le json, on va s'épargner 95% du modèle sous-jacent. On ne gérera donc pas ici les codes communes, etc. En bonus pour celles et ceux voulant tester ce que Postgres a dans le ventre, je proposerai une variante juste en dessous : +Partant d'un schéma nommé `insee`, on va créer deux tables :+1: +- la première contiendra la liste des *bases* disponibles, les différents volets du recensement ; +- lae seconde permettant de stocker les données. + +Pour rester concentré sur le JSON, on va s'épargner 95% du modèle sous-jacent. On ne gérera donc pas ici les codes communes, etc. En bonus pour celles et ceux voulant tester ce que PostgreSQL a dans le ventre, je proposerai une variante juste en dessous : ```sql BEGIN; From 866b91b628041b0a7f9ae3cdfd67725a20a8539d Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 20 Jan 2025 10:29:34 +0000 Subject: [PATCH 092/101] =?UTF-8?q?[pre-commit.ci]=20Corrections=20automat?= =?UTF-8?q?iques=20appliqu=C3=A9es=20par=20les=20git=20hooks.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md index e4406ccfce..3a9fef3e8d 100644 --- a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md +++ b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md @@ -143,7 +143,8 @@ En voici cependant un schéma succinct pour aider à la compréhension du reste *Ne vous préoccupez pas des tables empilées tout à droite, ce sont des partitions de la table `donnees_communes`. Le sujet ne sera pas évoqué ici et elles ne sont nécessaires pour l'exemple.* -Partant d'un schéma nommé `insee`, on va créer deux tables :+1: +Partant d'un schéma nommé `insee`, on va créer deux tables :+1: + - la première contiendra la liste des *bases* disponibles, les différents volets du recensement ; - lae seconde permettant de stocker les données. From 11ff48930742030829dec5caacdcf8077913149e Mon Sep 17 00:00:00 2001 From: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> Date: Mon, 20 Jan 2025 11:30:20 +0100 Subject: [PATCH 093/101] Update content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md Signed-off-by: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> --- .../2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md index 3a9fef3e8d..2304e66e2c 100644 --- a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md +++ b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md @@ -148,7 +148,7 @@ Partant d'un schéma nommé `insee`, on va créer deux tables :+1: - la première contiendra la liste des *bases* disponibles, les différents volets du recensement ; - lae seconde permettant de stocker les données. -Pour rester concentré sur le JSON, on va s'épargner 95% du modèle sous-jacent. On ne gérera donc pas ici les codes communes, etc. En bonus pour celles et ceux voulant tester ce que PostgreSQL a dans le ventre, je proposerai une variante juste en dessous : +Pour rester concentré sur le JSON, on va s'épargner 95% du modèle sous-jacent. On ne gérera donc pas ici les codes communes, etc. : ```sql BEGIN; From 200b9b5b6a4080ea4af8fa82a4dd0b2f94791c0b Mon Sep 17 00:00:00 2001 From: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> Date: Mon, 20 Jan 2025 11:35:26 +0100 Subject: [PATCH 094/101] Apply suggestions from code review Co-authored-by: Julien Signed-off-by: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> --- ...01-21_travailler-avec-JSON-et-PostgreSQL.md | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md index 2304e66e2c..c66e7ef39a 100644 --- a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md +++ b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md @@ -150,7 +150,7 @@ Partant d'un schéma nommé `insee`, on va créer deux tables :+1: Pour rester concentré sur le JSON, on va s'épargner 95% du modèle sous-jacent. On ne gérera donc pas ici les codes communes, etc. : -```sql +```sql title="Script de création des tables" linenums="1" BEGIN; -- table listant les differentes bases Insee disponibles @@ -191,22 +191,6 @@ INSERT INTO insee.bases (nom) VALUES ('rp_emploi') ``` -```sql - annee int2 NOT NULL, - fk_base int2 NOT NULL, - donnees jsonb NOT NULL, - UNIQUE (annee, code_commune, fk_base), - CONSTRAINT pk_donnees_communes PRIMARY KEY (pk_id, fk_base), - CONSTRAINT fk_donnees_bases FOREIGN KEY (fk_base) REFERENCES insee.bases (pk_id) ON UPDATE CASCADE ON DELETE CASCADE -) PARTITION BY LIST (fk_base); -CREATE TABLE insee.donnees_communes_fk_1 PARTITION OF insee.donnees_communes FOR VALUES IN (1); -CREATE TABLE insee.donnees_communes_fk_2 PARTITION OF insee.donnees_communes FOR VALUES IN (2); -CREATE TABLE insee.donnees_communes_fk_3 PARTITION OF insee.donnees_communes FOR VALUES IN (3); -CREATE TABLE insee.donnees_communes_fk_4 PARTITION OF insee.donnees_communes FOR VALUES IN (4); -CREATE TABLE insee.donnees_communes_fk_5 PARTITION OF insee.donnees_communes FOR VALUES IN (5); -CREATE TABLE insee.donnees_communes_fk_6 PARTITION OF insee.donnees_communes FOR VALUES IN (6); -END; -``` ## Insertion de données et récupération From 02dfcf6bb7b78b0cef4b8879f6dfa471ac3fa7d5 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 20 Jan 2025 10:35:54 +0000 Subject: [PATCH 095/101] =?UTF-8?q?[pre-commit.ci]=20Corrections=20automat?= =?UTF-8?q?iques=20appliqu=C3=A9es=20par=20les=20git=20hooks.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md | 1 - 1 file changed, 1 deletion(-) diff --git a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md index c66e7ef39a..3b8662f134 100644 --- a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md +++ b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md @@ -191,7 +191,6 @@ INSERT INTO insee.bases (nom) VALUES ('rp_emploi') ``` - ## Insertion de données et récupération Pour passer des données textuelles / SQL vers des données encodées en JSON dans le champ dédié qui va bien, PostgreSQL dispose de [*quelques* fonctions](https://docs.postgresql.fr/17/functions-json.html#FUNCTIONS-JSON-PROCESSING). Nous allons utiliser la fonction `jsonb_object()` qui permet de transformer un `array` sql sous forme `clef1, valeur1, clef2, valeur2 ....` en objet `jsonb` qui n'aura qu'un niveau d'imbrication. D'autres fonctions sont disponibles pour des objets plus complexes (comme `jsonb_build_object()`). From 0fee90298f3134929cfa7474389d11761595def5 Mon Sep 17 00:00:00 2001 From: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> Date: Mon, 20 Jan 2025 13:23:54 +0100 Subject: [PATCH 096/101] Apply suggestions from code review Co-authored-by: Julien Signed-off-by: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> --- .../2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md index 3b8662f134..b52977f1d3 100644 --- a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md +++ b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md @@ -239,9 +239,10 @@ Vous pouvez voir que j'utilise l'opérateur `?`; uniquement valable pour les cha Je précise que même si vous êtes encore ici, je suppose que vous savez déjà cela. Cependant, la forme `(quelque_chose)::(type)` est un raccourci de PostgreSQL pour faire un `cast` soit convertir une valeur dans un autre type. Avec `->>` la valeur nous est renvoyée sous forme de texte et nous la convertissons en entier. -### Avec du json imbriqué +### Avec du JSON imbriqué -Bon, il est bien gentil, mais là son json reste du json où tout est au premier niveau. Bah oui, pour mon besoin, cela m'a suffi. Mais, je ne vais pas fuir mes responsabilités et on va voir comment cela se passe avec du json plus complexe. Je repartirai de l'exemple de la partie précédente pour complexifier après cet aparté. +Bon, il est bien gentil, mais là son JSON reste du JSON où tout est au premier niveau. Bah oui, pour mon besoin, cela m'a suffi. Mais, je ne vais pas fuir mes responsabilités et on va voir comment cela se passe avec du JSON plus complexe. +Je repartirai de l'exemple de la partie précédente pour complexifier après cet aparté. Pour injecter du json complexe dans un champ, deux solutions s'offrent à nous : imbriquer les fonctions dédiées, ou caster une chaine de texte. Imaginons une table "test", dans un schema "test" et dont un champ `jsonb` se nomme "donnees". @@ -277,6 +278,8 @@ Pour plus d'informations sur les `json_path`, vous pouvez consulter la [document ## Attaquons nous au recensement +![logo INSEE](https://cdn.geotribu.fr/img/logos-icones/entreprises_association/INSEE.svg){: .img-thumbnail-left } + Bien, maintenant que nous avons essayé d'expliquer tant bien que mal les concepts avec des exemples simples, car il faut bien commencer quelque part, essayons avec quelque chose d'un peu plus volumineux. Récupérons le dernier millésime du volet population du recensement communal [au format csv sur le site de l'Insee](https://www.insee.fr/fr/information/8183122) (vous voulez les bases des principaux indicateurs). Pour l'exemple, on utilisera le fichier "Evolution et structure de la population". Il faut tout d'abord nettoyer les noms de champ. En effet, l'Insee indique le millésime systématiquement dans ses noms de champ, ce qui fait que ces derniers changent tous les ans pour un même indicateur. From 060d4d491fc96199cddfd63932ae4e331c369a50 Mon Sep 17 00:00:00 2001 From: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> Date: Mon, 20 Jan 2025 13:24:36 +0100 Subject: [PATCH 097/101] Update content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md Signed-off-by: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> --- .../2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md index b52977f1d3..1f748b93ab 100644 --- a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md +++ b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md @@ -424,7 +424,7 @@ Ouf. ![donnees_communes](https://cdn.geotribu.fr/img/articles-blog-rdp/articles/2024/postgresql_json/donnees_communes.png){: .img-center loading=lazy } -On veillera à bien grouper les inserts dans cet ordre annee/base/code_commune afin de faciliter la lecture des données par PostgreSQL (si votre table est partitionnée, cela sera facilité). +On veillera à bien grouper les inserts dans cet ordre annee/base/code_commune afin de faciliter la lecture des données par PostgreSQL. Maintenant, imaginez que comme l'auteur de ces lignes, vos gros doigts boudinnés cafouillent et glissent sur les touches lors de la rédaction de cette requête, qu'une faute de frappe s'y glisse, et là c'est le drame. Comment modifier le nom d'une clé déjà encodée dans la table ? Avec cette astuce : From a13d77075c07af779e2a0509e6870fb9a5cd0255 Mon Sep 17 00:00:00 2001 From: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> Date: Mon, 20 Jan 2025 15:10:31 +0100 Subject: [PATCH 098/101] Apply suggestions from code review Co-authored-by: Julien Signed-off-by: thomas-szczurek <121474664+thomas-szczurek@users.noreply.github.com> --- ...01-21_travailler-avec-JSON-et-PostgreSQL.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md index 1f748b93ab..d54ff786da 100644 --- a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md +++ b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md @@ -143,7 +143,7 @@ En voici cependant un schéma succinct pour aider à la compréhension du reste *Ne vous préoccupez pas des tables empilées tout à droite, ce sont des partitions de la table `donnees_communes`. Le sujet ne sera pas évoqué ici et elles ne sont nécessaires pour l'exemple.* -Partant d'un schéma nommé `insee`, on va créer deux tables :+1: +Partant d'un schéma nommé `insee`, on va créer deux tables : - la première contiendra la liste des *bases* disponibles, les différents volets du recensement ; - lae seconde permettant de stocker les données. @@ -181,7 +181,7 @@ Les plus tatillons des DBA d'entre vous auront remarqué ce `CHECK`. "Mais pourq Et on insère quelques lignes dans notre table insee.bases : -```sql +```sql title="Script SQL d'insertion de données" linenums="1" INSERT INTO insee.bases (nom) VALUES ('rp_population'), ('rp_menages'), @@ -466,9 +466,10 @@ ORDER BY fk_base, clef_json; La seule difficulté ici est la présence de [LATERAL](https://docs.postgresql.fr/17/queries-table-expressions.html#QUERIES-FROM). Ce « mot » permet d'utiliser un champ de la requête principale dans une sous-requête placée dans une clause FROM. Les éléments de la sous-requête seront ensuite évalués atomiquement avant d'être joints à la table d'origine. Oui, ce n'est pas très facile à expliquer. Ici, le WHERE de la sous-requête va interroger le champ `donnees` de la table "donnees_communes" pour voir s'il y voit la clef_json en train d'être évaluée dans la table "correspondance_clefs_champs" possédant l'alias "c". Si oui, alors on prend la valeur minimum/maximum du champ année et on le joint à cette ligne et uniquement à cette ligne. Puis évaluation de clef_json suivante ... (*évaluée atomiquement*) -Maintenant, on voudrait proposer un produit un peu plus simple d'utilisation avec tout ça. Excusez-moi d'avance, je copierai une requête de 50 lignes une seconde fois. La seule table utilisée ici que nous n'avons pas créée est une table zonages_administratifs comprenant les codes communes dans un champ "code_admin", et un champ "fk_type" contenant le type de zonage administratif (1 pour les communes). +Maintenant, on voudrait proposer un produit un peu plus simple d'utilisation avec tout ça. Excusez-moi d'avance, je copierai une requête de 50 lignes une seconde fois. +La seule table utilisée ici que nous n'avons pas créée est une table `zonages_administratifs` comprenant les codes communes dans un champ `code_admin` et un champ `fk_type` contenant le type de zonage administratif (1 pour les communes). -```sql +```sql title="Création de la vue matérialisée agrémentée" linenums="1" CREATE MATERIALIZED VIEW insee.donnees_communes_olap AS WITH -- Sélection des codes communes @@ -517,14 +518,13 @@ SELECT * FROM final WHERE valeur IS NOT NULL; ![vm_presence_cles_annees](https://cdn.geotribu.fr/img/articles-blog-rdp/articles/2024/postgresql_json/vm_donnees_olap.png){: .img-center loading=lazy } -Attention, la création de cette vue matérialisée ou son refresh peut prendre un certain temps si vous avez stocké beaucoup de données (1 heure chez moi pour les 6 volets de 2015 à 2021). +Attention, la création de cette vue matérialisée ou son rafraîchissement peut prendre un certain temps si vous avez stocké beaucoup de données (1 heure chez moi pour les 6 volets de 2015 à 2021). Enfin, histoire de vivre avec son temps et non comme un vieil ours des cavernes, on va convertir cette vue matérialisée en fichier [parquet](https://parquet.apache.org/). -Et, pour ça, on va utiliser gdal/ogr qui est décidément incroyable. +Et, pour ça, on va utiliser GDAL qui est décidément incroyable. -```sh -ogr2ogr -of parquet donnees_insee.parquet PG:"dbname='insee' shcema='insee' tables='donnees_communes_olap' user='nom_utilisateur' password='votre_mot_de_passe'" -``` +```sh title="Export vue au format parquet" +ogr2ogr -of parquet donnees_insee.parquet PG:"dbname='insee' schema='insee' tables='donnees_communes_olap' user='nom_utilisateur' password='votre_mot_de_passe'" Et on peut ainsi mettre le fichier sur un espace cloud, comme [ici](https://donnees-insee.s3.fr-par.scw.cloud/donnees_insee_olap.parquet) ! Vous pouvez ensuite sortir votre plus beau générateur de publications Linkedin qui mettra plein d'emojis choupi et faire le cake sur les rezos (imaginez que 90% du contenu de Linkedin doit être fait avec ces trucs qui sont capable de vous générer des publications expliquant que l'un des rares avantages du shape sur le geopackage est d'être un format multi-fichiers, le tout sur un ton très assuré). From a479dfb9c87f8c55e7c37cb112815921b83b2be6 Mon Sep 17 00:00:00 2001 From: Julien Date: Mon, 20 Jan 2025 15:31:10 +0100 Subject: [PATCH 099/101] Update content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md Signed-off-by: Julien --- .../2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md | 1 + 1 file changed, 1 insertion(+) diff --git a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md index d54ff786da..33e59dd5b8 100644 --- a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md +++ b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md @@ -525,6 +525,7 @@ Et, pour ça, on va utiliser GDAL qui est décidément incroyable. ```sh title="Export vue au format parquet" ogr2ogr -of parquet donnees_insee.parquet PG:"dbname='insee' schema='insee' tables='donnees_communes_olap' user='nom_utilisateur' password='votre_mot_de_passe'" +``` Et on peut ainsi mettre le fichier sur un espace cloud, comme [ici](https://donnees-insee.s3.fr-par.scw.cloud/donnees_insee_olap.parquet) ! Vous pouvez ensuite sortir votre plus beau générateur de publications Linkedin qui mettra plein d'emojis choupi et faire le cake sur les rezos (imaginez que 90% du contenu de Linkedin doit être fait avec ces trucs qui sont capable de vous générer des publications expliquant que l'un des rares avantages du shape sur le geopackage est d'être un format multi-fichiers, le tout sur un ton très assuré). From 44ac90a077848b2903b988c79a198ffb101fb026 Mon Sep 17 00:00:00 2001 From: GeoJulien Date: Mon, 20 Jan 2025 15:46:42 +0100 Subject: [PATCH 100/101] Revert "temp: disable RDP pages for 2025" This reverts commit ad5ea40ad74877a7e9e1859528b6f359e5702ad2. --- content/rdp/.pages | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/rdp/.pages b/content/rdp/.pages index 97af18fcc2..1c5f0e9842 100644 --- a/content/rdp/.pages +++ b/content/rdp/.pages @@ -1,7 +1,7 @@ title: "📰 Revues de presse" nav: - # - "2025" + - "2025" - "2024" - "2023" - "2022" From 140d97a9bbec301508756fe1204f03509d8067f8 Mon Sep 17 00:00:00 2001 From: GeoJulien Date: Mon, 20 Jan 2025 15:50:03 +0100 Subject: [PATCH 101/101] update(jsonPG): ajout titre sur bloc de code de la CTE --- .../2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md index 33e59dd5b8..5b12f51d56 100644 --- a/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md +++ b/content/articles/2025/2025-01-21_travailler-avec-JSON-et-PostgreSQL.md @@ -296,7 +296,7 @@ On va utiliser la CTE pour concaténer le nom que l'on veut donner à nos clés Notez que si votre table temporaire possède un nom différent de "rp_population_import" il vous faudra modifier la clause FROM de la CTE. -```sql +```sql title="L'INSERT le plus bizarre de votre vie (avec une CTE)" linenums="1" -- cte concatenant les données avec les clés et nettoyant les caractères spéciaux. WITH d AS ( SELECT