7 points par GN⁺ 2024-11-13 | 2 commentaires | Partager sur WhatsApp
  • La documentation officielle de Postgres est excellente, mais le PDF de Postgres 17 fait 3 200 pages, ce qui rend difficile pour un débutant d’apprendre uniquement via la doc, avant la pratique, la conception de schémas, le fonctionnement de SQL et les pièges d’exploitation
  • Sauf raison particulière, il faut normaliser les données ; la dénormalisation, qui consiste à dupliquer des données pour accélérer la lecture, a un coût en incohérences et en complexité d’écriture
  • Les mots-clés SQL ne sont pas sensibles à la casse, mais NULL signifie plutôt « inconnu » : le comparer comme le null d’un langage généraliste produit des résultats contre-intuitifs
  • Avec psql, le simple fait de bien utiliser le pager, \x, .psqlrc, \pset null, l’autocomplétion, les commandes en backslash et \copy améliore nettement la lisibilité de la sortie, l’exploration et l’export CSV
  • Les index, les verrous, les transactions et JSONB sont puissants, mais sans compréhension du plan de requête et des contraintes d’exploitation, ils peuvent entraîner des baisses de performance ou des problèmes de disponibilité

Contexte à connaître avant d’attaquer l’immense documentation officielle

  • La documentation officielle de Postgres représente, pour la version 17 actuelle, 3 200 pages en PDF au format US letter, et 3 024 pages au format A4
  • Il existe beaucoup de connaissances pratiques utiles à avoir avant d’utiliser Postgres ; certaines s’appliquent aussi à d’autres SGBD SQL, mais leur portée n’est pas toujours évidente

Normaliser les données par défaut

  • La normalisation est le processus consistant à supprimer les données redondantes ou inutiles dans un schéma de base de données
  • Si la table documents stocke directement user_email, alors quand un utilisateur change d’adresse e-mail, il faut mettre à jour toutes les lignes de document de cet utilisateur
    • À la place, on peut faire en sorte que chaque ligne de documents référence une ligne d’une autre table, comme users, via une clé étrangère user_id
  • Il n’est pas nécessaire de mémoriser toutes les formes normales comme la “1st normal form”, mais un processus de normalisation classique mène souvent à des schémas plus faciles à maintenir
  • La dénormalisation consiste à dupliquer des données pour les lire rapidement sans les recalculer à chaque fois
    • Dans une application de gestion des plannings d’employés, on peut, au lieu de recalculer à chaque fois le cumul annuel des heures travaillées en additionnant toutes les durées de shift, le calculer et le stocker périodiquement ou lors d’un changement d’horaire
    • Ces données peuvent être stockées dans Postgres lui-même ou dans une couche de cache comme Redis
  • La dénormalisation a presque toujours un coût ; les plus courants sont la possibilité d’incohérences de données et l’augmentation de la complexité des écritures

Les conseils « à ne pas faire » du projet Postgres

  • Le wiki officiel de Postgres propose une liste « Don’t do this »
  • Ce n’est pas grave si vous ne comprenez pas tous les points, et ceux que vous ne comprenez pas sont aussi ceux pour lesquels vous avez le moins de chances de faire l’erreur
  • En particulier, ces conseils valent la peine d’être retenus

Comportements SQL qui prêtent facilement à confusion

  • Les mots-clés SQL n’ont pas besoin d’être en majuscules

    • Les mots-clés SQL ne sont pas sensibles à la casse
    • Les requêtes suivantes ont le même sens
    SELECT * FROM my_table WHERE x = 1 AND y > 2 LIMIT 10;
    select * from my_table where x = 1 and y > 2 limit 10;
    SELECT * from my_table WHERE x = 1 and y > 2 LIMIT 10;
    
    • Cette propriété n’est pas propre à Postgres
  • NULL est différent du null/nil des langages généralistes

    • En SQL, NULL est plus proche de « inconnu » que du null ou nil des langages de programmation classiques
    • NULL = NULL ne renvoie pas true, mais NULL
    • Une comparaison dont l’un des côtés est NULL renvoie généralement aussi NULL
    • Pour comparer avec NULL, il faut utiliser les opérations suivantes
      • x IS NULL : true si x vaut NULL
      • x IS NOT NULL : true si x n’est pas NULL
      • x IS NOT DISTINCT FROM y : similaire à x = y, mais traite NULL comme une valeur ordinaire
      • x IS DISTINCT FROM y : similaire à x != y/x <> y, mais traite NULL comme une valeur ordinaire
    • La clause WHERE ne renvoie des lignes que lorsque la condition vaut true
      • SELECT * FROM users WHERE title != 'manager' ne renvoie pas les lignes où title vaut NULL
      • parce que NULL != 'manager' renvoie NULL
    • COALESCE renvoie la première valeur non NULL parmi plusieurs arguments
    COALESCE(NULL, 5, 10) = 5
    COALESCE(2, NULL, 9) = 2
    COALESCE(NULL, NULL) IS NULL
    

Mieux utiliser psql

  • Améliorer la lisibilité de la sortie

    • Si l’affichage est difficile à lire quand vous interrogez une table avec beaucoup de colonnes ou des valeurs longues, il est possible que le pager soit désactivé
    • Le pager du terminal permet de faire défiler de gros volumes de texte ou des tables psql dans la fenêtre d’affichage
    • Pour les tables avec beaucoup de colonnes, vous pouvez activer le mode étendu avec \pset expanded ou \x
    • Si vous voulez l’utiliser par défaut, ajoutez \x dans ~/.psqlrc dans votre répertoire personnel
  • Rendre l’affichage de NULL plus explicite

    • La configuration par défaut ne montre pas clairement si une valeur est NULL dans la sortie
    • Vous pouvez définir une chaîne d’affichage pour NULL dans psql
    \pset null '[NULL]'
    
    • Une chaîne Unicode fonctionne aussi ; pour l’utiliser par défaut, ajoutez la même commande à ~/.psqlrc
  • Exploiter l’autocomplétion et les commandes en backslash

    • psql prend en charge l’autocomplétion comme une console interactive
    • En tapant une partie d’un mot-clé ou d’un nom de table puis en appuyant sur Tab, on peut compléter le reste
    • Voici quelques commandes en backslash utiles
      • \? : liste de tous les raccourcis
      • \d : affiche les relations, c’est-à-dire les tables et séquences, ainsi que leur propriétaire
      • \d+ : ajoute la taille et certaines métadonnées à \d
      • \d table_name : affiche le schéma de la table, le type des colonnes, leur caractère nullable, les valeurs par défaut, les index et les contraintes de clé étrangère
      • \e : édite la requête dans l’éditeur par défaut défini par la variable d’environnement $EDITOR
      • \h SQL_KEYWORD : affiche la syntaxe du mot-clé SQL correspondant et un lien vers la documentation
  • Export CSV et alias dans SELECT

    • \copy permet d’enregistrer le résultat d’une requête en CSV
    \copy (select * from some_table) to 'my_file.csv' CSV
    
    • Pour inclure les noms de colonnes sur la première ligne, ajoutez l’option HEADER
    \copy (select * from some_table) to 'my_file.csv' CSV HEADER
    
    • \copy permet d’éviter les privilèges élevés requis par l’instruction plus standard COPY
    • Les colonnes renvoyées par SELECT peuvent recevoir un alias via AS
    SELECT vendor, COUNT(*) AS number_of_backpacks
    FROM backpacks
    GROUP BY vendor
    ORDER BY number_of_backpacks DESC;
    
    • Dans GROUP BY et ORDER BY, on peut référencer le numéro des colonnes apparues après SELECT
    SELECT vendor, COUNT(*) AS number_of_backpacks
    FROM backpacks
    GROUP BY 1
    ORDER BY 2 DESC;
    
    • Cette forme abrégée est pratique, mais il vaut mieux éviter de l’utiliser dans des requêtes déployées en production

Ajouter un index ne veut pas dire qu’il sera toujours utilisé

  • Index et plan de requête

    • Un index est une structure de données jouant le rôle de répertoire de raccourci pour retrouver des lignes d’une table à partir de champs donnés
    • L’index le plus courant est le B-tree, qui fonctionne pour les conditions d’égalité exactes comme WHERE a = 3 et les conditions de plage comme WHERE a > 5
    • On ne peut pas dire directement à Postgres d’utiliser un index précis
    • Postgres prédit, à partir des statistiques qu’il maintient sur chaque table, si un index sera plus rapide qu’un sequential scan lisant la table de bout en bout
    • En ajoutant EXPLAIN devant SELECT ... FROM ..., on peut voir le plan de requête que Postgres prévoit d’utiliser
    • Pour apprendre à lire un plan de requête, on peut consulter le guide de thoughtbot sur EXPLAIN ANALYZE, la documentation de pganalyze, la documentation officielle et explain.depesz.com
  • Petites tables et index multicolonnes

    • Sur des tables avec peu de lignes, comme dans une base de développement locale, un index peut ne pas être d’une grande aide
    • Avec une centaine de lignes, Postgres peut juger qu’un sequential scan est plus rapide qu’un index
    • Postgres prend en charge les index multicolonnes
    CREATE INDEX CONCURRENTLY ON tbl (a, b);
    
    • Une condition comme WHERE a = 1 AND b = 2 peut être plus rapide qu’avec deux index séparés, l’un sur a et l’autre sur b
    • Parce qu’un seul parcours du B-tree permet de combiner efficacement les conditions de recherche
    • Un index (a, b) accélère aussi les requêtes filtrant uniquement sur a, aussi bien qu’un index sur a seul
    • Une requête comme WHERE b = 5 peut être accélérée, mais pas forcément de façon optimale
      • L’index est ordonné d’abord par a, puis par b, donc il faut parcourir toutes les valeurs de a pour trouver celles de b
    • Si vous interrogez souvent plusieurs combinaisons de colonnes, il est fréquent d’avoir à la fois un index (a, b) et un index sur b seul
    • Selon le cas, on peut aussi s’appuyer sur des index séparés sur a et b
  • Utiliser text_pattern_ops pour les correspondances par préfixe

    • Avec une approche de type materialized path pour stocker un arbre de répertoires, on peut vouloir retrouver tous les descendants commençant par un certain préfixe
    SELECT * FROM directories WHERE path LIKE '/1/2/3/%'
    
    • Même si vous créez un index B-tree par défaut sur la colonne path, cette requête peut ne pas l’utiliser
    CREATE INDEX CONCURRENTLY ON directories (path);
    
    • Pour permettre l’ordre caractère par caractère nécessaire aux correspondances par préfixe ou par motif, il faut spécifier une operator class
    CREATE INDEX CONCURRENTLY ON directories (path text_pattern_ops);
    

Les problèmes d’exploitation causés par les verrous et les transactions

  • Les verrous dans Postgres

    • Un verrou (lock) ou mutex est un mécanisme permettant de faire en sorte qu’une seule cliente ou un seul client exécute à la fois une opération à risque
    • Dans une base de données, les mises à jour d’objets comme les lignes, tables ou vues doivent soit toutes réussir, soit toutes échouer ; pour éviter qu’une exécution concurrente n’en réussisse qu’une partie, des verrous sont pris sur les objets concernés
    • Les niveaux de verrou de table dans Postgres vont du moins restrictif au plus restrictif
      • ACCESS SHARE : SELECT
      • ROW SHARE : SELECT ... FOR UPDATE
      • ROW EXCLUSIVE : UPDATE, DELETE, INSERT
      • SHARE UPDATE EXCLUSIVE : CREATE INDEX CONCURRENTLY
      • SHARE : CREATE INDEX, sans CONCURRENTLY
      • ACCESS EXCLUSIVE : de nombreuses formes de ALTER TABLE, ALTER INDEX
    • Sur une même table, les opérations suivantes peuvent coexister ou doivent attendre
      • UPDATE pendant un SELECT : possible
      • UPDATE pendant un CREATE INDEX CONCURRENTLY : possible
      • SELECT pendant un CREATE INDEX : possible
      • SELECT pendant un ALTER TABLE : doit généralement attendre
      • ALTER TABLE pendant un SELECT : doit généralement attendre
    • Certaines formes de ALTER TABLE demandent des verrous plus faibles ; pour les détails complets, voir la documentation officielle sur les verrous explicites et ce guide des conflits de verrous par opération
  • ALTER TABLE lent et file d’attente de verrous

    • Si un ALTER TABLE prend longtemps, il peut aussi bloquer les SELECT qui lisent la même table
    • S’il s’agit d’une table centrale comme users, consultée par toutes les requêtes d’une application web, les requêtes peuvent attendre jusqu’au timeout et renvoyer des 503
    • Voici quelques causes fréquentes de lenteur d’un ALTER TABLE
      • ajout d’une colonne avec une valeur par défaut non constante
      • changement de type de colonne
      • ajout d’une contrainte d’unicité
    • Depuis Postgres 11, le problème qui rendait lentes toutes les valeurs par défaut lors de l’ajout d’une colonne a été corrigé ; les valeurs par défaut non constantes peuvent toutefois rester problématiques
    • Même si ALTER TABLE est en lui-même rapide, il ne s’exécute pas tant qu’il n’a pas obtenu le verrou
      • si un vieux SELECT lent lancé depuis longtemps tourne encore sur un tableau de bord interne, ALTER TABLE doit attendre
    • Les verrous Postgres forment une file d’attente, donc les requêtes ultérieures sur la même table, arrivées derrière un ALTER TABLE en attente, peuvent elles aussi être bloquées
    • Le même scénario est détaillé dans Migrations and exclusive locks
  • Les transactions longues sont aussi dangereuses

    • Une transaction permet de regrouper plusieurs instructions de base de données en mode tout-ou-rien ; elle commence par BEGIN et se termine par COMMIT
    • Les changements effectués pendant la transaction ne sont pas visibles des autres clientes et clients, puis deviennent publics au COMMIT
    • C’est adapté aux opérations comme un virement, où la diminution du solde d’un compte et l’augmentation du solde d’un autre doivent réussir ou être annulées ensemble
    • Lorsqu’une transaction obtient un verrou, elle le conserve jusqu’au COMMIT
    • Si vous faites BEGIN, puis UPDATE sur une certaine ligne avant de laisser la session ouverte, un DELETE sur cette même ligne depuis un autre client restera bloqué jusqu’au commit
    • Des transactions laissées ouvertes plus longtemps que nécessaire peuvent bloquer les requêtes ou mises à jour d’autres clients

JSONB est un outil tranchant

  • Problèmes de performance et de schéma avec JSONB

    • JSONB est flexible, mais mal utilisé il a de gros inconvénients
    • Postgres ne suit pas les statistiques d’une colonne JSONB, donc une requête d’égalité sur une seule colonne JSONB peut être bien plus lente qu’une requête équivalente sur un ensemble de colonnes classiques
    • Un exemple montre un cas où JSONB est 2000 fois plus lent
    • Une colonne JSONB peut contenir à peu près n’importe quoi, ce qui la rend puissante, mais offre peu de garanties sur la structure
    • Avec une table classique, on peut regarder le schéma et prédire le résultat d’une requête ; avec JSONB, il n’est pas toujours clair si les noms de clé sont en camelCase ou en snake_case, ni si un état est un booléen ou un enum
    • Les propriétés de typage statique des données Postgres classiques ne s’appliquent pas de la même façon à JSONB
  • Les comparaisons de type en JSONB sont maladroites

    • Si vous cherchez dans la colonne JSONB data de la table backpacks les lignes dont le champ brand vaut JanSport, la requête suivante ne fonctionne pas
    select * from backpacks where data['brand'] = 'JanSport';
    
    • Postgres s’attend à ce que le type à droite de la comparaison corresponde à celui de gauche, et la partie droite doit être un document JSON valide
    • Un document JSON doit être un objet, un tableau, une chaîne, un nombre, un booléen ou null ; JanSport tout seul n’est donc pas un JSON valide
    • La bonne approche consiste soit à comparer à une chaîne JSON, soit à convertir la partie gauche en text Postgres
    select * from backpacks where data['brand'] = '"JanSport"';
    
    select * from backpacks where data['brand'] = '"JanSport"'::jsonb;
    
    select * from backpacks where data->>'brand' = 'JanSport';
    
    • Le NULL de SQL et le null de JSONB n’ont pas le même comportement
      • 'null'::jsonb = 'null'::jsonb renvoie true, alors que NULL = NULL renvoie NULL
    • JSONB dispose de nombreux opérateurs et fonctions dédiés, qu’il est difficile de mémoriser d’un coup
    • Postgres propose à la fois JSON, qui stocke la valeur JSON sous forme de texte, et JSONB, qui la convertit dans un format binaire plus efficace
    • JSONB présente des avantages, notamment la possibilité d’indexer, tandis que le format JSON relève plutôt de cas particuliers

2 commentaires

 
bbulbum 2024-11-19

J’aimerais bien lire un jour ce qu’il ne faut pas faire.

 
GN⁺ 2024-11-13
Commentaires sur Hacker News
  • PostgreSQL est globalement sensible à la casse, mais écrire les mots-clés SQL en majuscules est surtout un moyen d’améliorer la lisibilité par reconnaissance visuelle des motifs
    Ce n’est pas indispensable, mais si je devais déboguer la requête de quelqu’un d’autre, je la passerais probablement dans un prettifier pour parcourir rapidement la définition sans buter sur des détails de forme syntaxique
    Comme pour le formatage du code dans d’autres langages, une structure visuelle cohérente, comme une indentation uniforme, réduit le temps passé à comprendre les parties évidentes et permet de se concentrer sur l’essentiel
    En revanche, je déteste vraiment mélanger les majuscules et les minuscules dans les identifiants, comme actuallyUsingCaseInIdentifiers, et je n’ai aucune envie de voir des colonnes qui nécessitent des guillemets doubles pour être inspectées en CLI

    • Les identifiants en majuscules ressemblent à des blocs interchangeables, ce qui ralentit la lecture par rapport à la forme des mots en minuscules
    • Quand on manipule SQL de manière interactive, connaître cette distinction est assez utile
      Si je tape rapidement une requête temporaire que personne ne verra et que je vais jeter ensuite, je ne fais pas attention à la casse, mais pour le SQL qui sera commit dans un dépôt, j’écris les commandes en ALL CAPS
    • Je comprends les majuscules comme une forme de coloration syntaxique à l’époque des écrans noir et blanc
      Ce n’est plus nécessaire aujourd’hui avec la couleur, mais je n’ai pas de source, c’est juste un vieux souvenir
    • PostgreSQL replie les identifiants en minuscules alors que le standard les replie en majuscules ; de ce point de vue, il ne respecte donc pas le standard sur la gestion de la casse
      Cela dit, il ne faut de toute façon pas mélanger identifiants entre guillemets et non guillemetés, et l’inspection des structures internes n’est généralement pas standardisée non plus, donc ce n’est pas très important
    • Je serais curieux d’avoir des recommandations de prettifier ou de linter pour SQL
  • Je viens de découvrir la rubrique « don’t do this » du wiki PostgreSQL, et elle est assez utile : https://wiki.postgresql.org/wiki/Don%27t_Do_This

    • Si ces fonctionnalités sont des pièges aussi faciles, je me demande pourquoi elles ne sont pas marquées comme obsolètes
      Par exemple, il semblerait logique de désactiver par défaut des fonctions comme l’héritage de tables dans les nouveaux schémas, et d’exiger une configuration volontairement complexe pour les réactiver
    • Ça rappelle SQL Anti-patterns, qui à mon avis est un livre que toute personne travaillant avec des bases de données devrait lire
    • Cela me fait reconsidérer quelques habitudes apprises du côté de MySQL
  • Beaucoup de choses mentionnées ici ne concernent pas uniquement PostgreSQL
    C’est le cas du comportement étrange de NULL, de l’ordre des colonnes d’index, etc., et en particulier l’interaction entre NULL et les index/contraintes d’unicité n’est pas intuitive non plus dans MySQL
    Par exemple, si on a une table utilisateur où email ne peut pas être NULL et username peut l’être, et qu’on met une contrainte d’unicité sur (email, username), on peut insérer plusieurs fois le même email avec username à NULL. En effet, NULL n’est pas égal à un autre NULL

    • À noter qu’à partir de PostgreSQL 15, on peut influencer ce comportement dans les contraintes et index uniques avec NULLS [NOT] DISTINCT
      https://www.postgresql.org/docs/devel/sql-createtable.html#S...
    • Je trouve ce comportement par défaut pragmatiquement correct
      Les cas d’usage nécessitant le comportement inverse sont bien plus rares
  • Se contenter de dire « normalisez vos données sauf bonne raison de ne pas le faire » n’est pas suffisant
    Même dans la page liée par l’auteur, il y a 11 formes normales en comptant les formes non normales ; la plupart des gens ne savent pas ce que c’est, et parmi elles, 7 ne serviront jamais
    Il ne faut pas pousser les gens à partir à la chasse aux formes normales plus élevées

    • Cela dit, l’auteur ajoute généralement un paragraphe expliquant ce qu’il veut dire, et cette approche me semble juste
      J’ai aussi dû corriger quelques problèmes de ce genre dans un projet que je viens de reprendre ; il y a très peu de raisons de dupliquer les données
    • Si l’article s’adresse à des débutants, alors en cas de doute, la réponse est presque toujours la troisième forme normale
    • La règle générale consiste à normaliser autant que possible, puis à dénormaliser jusqu’à obtenir les performances nécessaires
  • Le premier conseil est de faire un VACUUM tous les jours
    Quand j’ai débuté, je ne le savais pas, donc je n’ai jamais fait de VACUUM sur la base de données de reddit, puis un jour j’ai dû le lancer sans avoir le choix ; reddit est alors resté presque indisponible pendant une journée entière, le temps que cela se termine

    • Il n’y avait apparemment pas d’autovacuum
      À l’échelle de reddit, il est surprenant que les ID de transaction ne se soient pas épuisés avant cela
  • J’aimerais que les développeurs se préoccupent davantage de la normalisation et arrêtent de tout entasser dans des colonnes JSONB

    • Bien avant que les bases de données puissent stocker du JSON structuré, les développeurs juniors débattaient déjà avec passion, souvent de façon très théorique, du bon niveau de normalisation
      Les développeurs plus expérimentés savaient que la bonne réponse consistait à ne rien dupliquer, sauf les clés, et à ne dénormaliser qu’en tout dernier recours
      Puis des bases comme Mongo sont arrivées, proposant des « sortes de bases de données » où la normalisation est difficile ou dénuée de sens, ce qui a encouragé ce genre de juniors ; il en a résulté, pendant un temps, des conceptions de bases de données atroces et des amas de déchets impossibles à maintenir
      Aujourd’hui, le balancier est revenu et on redécouvre les avantages des bases de données normalisées, mais les colonnes JSON restent une porte de sortie où les mauvaises pratiques peuvent encore prospérer
    • Il y a deux raisons d’utiliser une colonne JSONB
      Premièrement, pour stocker du JSON. Quand un serveur web appelle une API tierce, enregistrer la réponse API brute dans une colonne JSONB puis la traiter à partir de là laisse une trace exploitable pour déboguer les problèmes venant de cette API
      Deuxièmement, pour stocker des types somme (sum types). Le fait que SQL ne prenne pas en charge les types somme est sans doute l’un des plus gros défauts quand il s’agit de modéliser des données dans une base SQL
      Il existe plusieurs contournements, et « le mettre simplement dans une colonne JSONB et le valider dans l’application » en est un, mais aucun de ces contournements n’est particulièrement brillant
    • Même en faisant attention à la normalisation, on finit souvent par avoir un tiroir JSONB fourre-tout
      Tant qu’on ne se met pas à écrire de mauvaises requêtes dans le JSONB au lieu de remonter ces valeurs dans des colonnes séparées, je ne considère pas cela comme un gros problème en soi
    • La plupart des développeurs qui utilisent ce genre d’outils aujourd’hui construisent en pratique leur propre système de gestion de base de données, en laissant simplement la persistance à un autre SGBD
      Du moment que les exigences de persistance sont satisfaites, il n’y a pas de forte pression pour réfléchir à une bonne conception
      Je ne sais pas si construire un SGBD au-dessus d’un autre SGBD est une bonne idée, mais c’est en tout cas l’état actuel des choses
    • Pour que cette approche fonctionne correctement, il faut aussi une procédure de migration de schéma qui permette d’annuler les changements de schéma
      Si une nouvelle colonne détruit les performances ou pose problème, il faut pouvoir revenir en arrière
      Si un outil CLI est impliqué, il faut aussi gérer le niveau de downtime acceptable, déterminer si une mise à jour synchronisée des versions à l’échelle de toute l’entreprise est possible, ou s’il faut supporter à la fois l’ancien et le nouveau schéma pendant un temps
      Si la base de données ne fait pas partie du produit principal de l’équipe, il est possible que tout cela soit totalement absent
  • J’ai écrit ceci pour aider les débutants : https://tomcam.github.io/postgres/

  • Le texte est vraiment excellent, et je ne savais pas que la documentation PostgreSQL faisait 3200 pages
    Je l’utilise depuis un moment déjà, en apprenant au fur et à mesure des besoins ; j’aime bien la documentation officielle, et j’aime aussi lire des articles ciblés quand un sujet précis devient pertinent
    Ce serait utile pour les lecteurs si l’auteur ajoutait à https://challahscript.com/what_i_wish_someone_told_me_about_... qu’un index sur les colonnes (b, a) fonctionne bien aussi pour les recherches sur b seul
    C’est déjà plus ou moins suggéré quand il parle des recherches sur a seul, mais l’expliciter ne ferait pas de mal
    Je n’utilise presque jamais les parties JSON/JSONB, donc je ne les vois pas souvent

  • Quand je repense aux SQL grotesques que j’ai vus en conditions réelles, je me dis qu’il vaudrait mieux commencer par lire le papier de Codd et comprendre ce qu’est le modèle relationnel
    Il ne fait que 11 pages, et rien que sa lecture réduirait la somme de souffrance dans ce monde

  • Presque tout ce texte s’applique aussi à d’autres bases de données MVCC comme MySQL
    Les détails peuvent varier, mais MySQL souffre aussi des longues transactions et de problèmes amusants du même genre, comme les verrous de métadonnées pris pendant ALTER