7 points par GN⁺ 2024-09-26 | 2 commentaires | Partager sur WhatsApp
  • Une liste de bonnes habitudes d’écriture SQL et de motifs de requêtes souvent utiles en analyse de données, avec l’idée de départ qu’elles ne s’appliquent pas forcément de la même manière à tous les SGBDR
  • Côté lisibilité, il est recommandé d’utiliser les virgules en tête de ligne, WHERE 1=1, l’indentation, les CTE, les commentaires et USING pour rendre les requêtes plus faciles à lire et à modifier
  • Pour le traitement des données, l’article présente avec exemples des syntaxes comme anti-join, QUALIFY, GROUP BY ROLLUP et EXCEPT, utilisées en pratique pour filtrer des résultats, produire des totaux ou comparer des tables
  • Côté performance et exactitude, un NOT IN avec des NULL, les conversions implicites de type et les conflits d’alias sur des champs calculés peuvent fausser les résultats ou ralentir les requêtes
  • Dans les requêtes complexes, des habitudes de base comme comprendre l’ordre d’exécution, consulter la documentation, indiquer l’origine des colonnes et donner un nom explicite aux requêtes enregistrées améliorent le débogage et la réutilisabilité

Habitudes pour améliorer la lisibilité en SQL

  • Ce dépôt rassemble une série de conseils et astuces SQL accumulés sur plusieurs années, centrés sur ce qui est utile au quotidien pour un data analyst et sur ce qu’il aurait été bon de savoir au moment de commencer à écrire du SQL
  • Certaines astuces peuvent ne pas convenir à tous les SGBDR
  • Virgules et AND en tête de ligne

    • Dans la clause SELECT, il est recommandé d’utiliser des virgules en tête de ligne plutôt que des virgules finales pour séparer les champs
    • Cela permet de voir plus clairement s’il s’agit d’une nouvelle colonne ou simplement d’un retour à la ligne dans le code
    • Même avec des lignes de longueurs différentes, il devient plus facile de repérer une virgule oubliée
    • Pour la même raison, on peut aussi placer AND en tête des conditions dans la clause WHERE
  • Faciliter les tests de conditions avec WHERE 1=1

    • Ajouter la condition factice 1=1 dans la clause WHERE permet de commenter des conditions pendant les tests sans casser la requête
    • Même si toutes les autres conditions sont commentées, 1=1 reste en place et la requête peut toujours s’exécuter
  • Indentation et formateurs

    • Une bonne indentation aide les collègues comme votre futur vous-même à relire plus facilement une requête
    • S’il existe une convention d’équipe ou d’entreprise, mieux vaut la suivre ; sinon, il est préférable d’adopter une méthode qui vous convient
    • On peut utiliser le formateur en ligne poorsql ou le linter sqlfluff
  • Envisager des CTE pour les requêtes complexes

    • Quand on imbrique des vues inline sur plus de 2 ou 3 niveaux, on obtient facilement une requête difficile à comprendre quelques semaines plus tard
    • Les CTE sont présentées comme un moyen de structurer les longues requêtes, tout en améliorant la réutilisation et le débogage
  • Les commentaires doivent expliquer le « pourquoi »

    • Avec le temps, il peut devenir difficile de se rappeler pourquoi un traitement particulier a été ajouté
    • En général, les commentaires devraient expliquer pourquoi le code a été écrit ainsi, plutôt que seulement comment il fonctionne
    • L’exemple ajoute un commentaire à une condition qui exclut des contenus archive, parce que le nouveau CMS ne peut pas gérer le format vidéo archive
  • Utiliser USING pour joindre des colonnes de même nom

    • Quand deux tables sont jointes sur une colonne portant le même nom, USING permet d’exprimer la jointure plus simplement que ON
    • USING supprime le doublon de la colonne commune dans le résultat et n’en renvoie qu’une seule version
    • Avec ON, si l’on ne précise pas explicitement la colonne commune, une erreur ambiguous column name peut survenir

Syntaxes utiles pour le traitement des données

  • Trouver les lignes absentes d’une autre table avec un anti-join

    • Un anti-join sert à renvoyer les lignes présentes dans une table mais sans correspondance dans une autre
    • L’exemple traite le cas où l’on veut récupérer uniquement les video_id de contenus non archivés
    • Il existe plusieurs façons de l’implémenter
    • faire un LEFT JOIN, puis filtrer uniquement les lignes dont la clé de la table jointe est NULL
    • utiliser NOT IN avec une sous-requête
    • utiliser NOT EXISTS avec une sous-requête corrélée
    • L’usage de NOT IN n’est pas recommandé, car les valeurs NULL peuvent l’empêcher de fonctionner comme prévu
  • Filtrer le résultat d’une fonction de fenêtre avec QUALIFY

    • QUALIFY permet de filtrer le résultat d’une requête à partir du résultat d’une fonction de fenêtre
    • Cela évite de passer par une vue inline et réduit le nombre de lignes de code
    • L’exemple sélectionne les 10 meilleurs marchés par produit avec DENSE_RANK(), puis filtre avec QUALIFY
    • Limitation mentionnée : QUALIFY semble n’être disponible que dans de grands data warehouses comme Snowflake, Amazon Redshift et Google BigQuery
  • GROUP BY et ORDER BY basés sur la position des colonnes

    • Au lieu d’utiliser les noms de colonnes, on peut écrire GROUP BY 1 ou ORDER BY 2 en se basant sur la position des colonnes
    • Cela peut être pratique pour des requêtes temporaires ou à usage unique
    • En code de production, il est recommandé de toujours référencer directement les noms de colonnes
  • Produire des totaux avec GROUP BY ROLLUP

    • GROUP BY ROLLUP peut servir à générer des sous-totaux et un total général
    • L’exemple calcule la somme des salaires par département et ajoute aussi une ligne avec le total global
    • La documentation Transact-SQL explique que ROLLUP crée des groupes selon des combinaisons d’expressions de colonnes et réduit le nombre de groupes de droite à gauche pour produire sous-totaux et total général
    • En appliquant COALESCE, on peut afficher la ligne de total sous une valeur comme Total
    • Il faut aussi faire attention à la colonne de tri pour que la ligne de total apparaisse en bas du résultat
  • Trouver les différences entre deux jeux de résultats avec EXCEPT

    • EXCEPT renvoie les lignes présentes dans le résultat de la première requête mais absentes du résultat de la seconde
    • En combinant EXCEPT et UNION ALL, on peut vérifier si deux tables contiennent exactement les mêmes données
    • Si aucune ligne n’est renvoyée, les deux tables sont identiques
    • Si des lignes sont renvoyées, ce sont elles qui expliquent les écarts

Motifs qui nuisent à la performance et à l’exactitude

  • Sur des colonnes pouvant contenir NULL, NOT EXISTS vaut mieux que NOT IN

    • Si la colonne comparée autorise NULL, NOT IN peut souvent être plus lent que NOT EXISTS
    • Ce comportement a été observé sur Snowflake, et le Don’t Do This du wiki PostgreSQL indique que NOT IN (SELECT ...) est souvent mal optimisé
    • NOT IN ne fonctionne pas comme attendu si des valeurs NULL sont présentes dans les données comparées
    • Le fait qu’une colonne autorise NULL ne signifie pas qu’elle en contienne réellement, mais lorsqu’on travaille sur des tables qu’on ne peut pas modifier, NOT EXISTS peut aider à améliorer les performances
  • Les conversions implicites de type peuvent ralentir ou échouer

    • Si l’on compare une colonne à une valeur d’un autre type de données, la base peut tenter une conversion implicite de type
    • L’exemple traite le cas d’une comparaison entre un entier 200050 et une colonne video_id de type chaîne
    • S’appuyer sur des conversions implicites peut poser problème
    • une erreur peut se produire si certaines valeurs ne peuvent pas être converties
    • la requête peut ralentir à cause du travail supplémentaire nécessaire pour convertir chaque valeur vers le type demandé
    • Il est préférable d’utiliser le même type de données que la colonne ou, pour éviter les erreurs, une fonction comme TRY_TO_NUMBER de Snowflake
    • L’impact sur les performances dépend de la taille du jeu de données traité

Erreurs fréquentes

  • NOT IN et NULL

    • NOT IN ne fonctionne pas si les valeurs comparées contiennent NULL
    • Comme NULL représente Unknown, le moteur SQL ne peut pas vérifier qu’une valeur testée n’est pas dans la liste
    • Dans ce cas, NOT EXISTS est une alternative
  • Conflits d’alias sur des champs calculés

    • Donner à un champ calculé le même nom qu’une colonne existante peut entraîner un comportement inattendu
    • La documentation GROUP BY de Snowflake précise que si un nom dans la clause GROUP BY correspond à la fois à un nom de colonne et à un alias, le nom de colonne est utilisé
    • Dans l’exemple, si l’on crée l’alias LEFT(product, 1) AS product puis qu’on écrit GROUP BY product, le regroupement se fait sur la colonne product d’origine au lieu de sa première lettre, ce qui renvoie 3 lignes
    • Deux solutions sont proposées
    • utiliser un alias unique comme product_letter
    • expliciter l’expression, par exemple GROUP BY LEFT(product, 1)
    • Des problèmes d’alias peuvent aussi apparaître avec les fonctions de fenêtre
    • Dans l’exemple, un CASE remplace le revenue de Robot par 0, mais ce changement est appliqué après l’exécution de la fonction de fenêtre, si bien que le classement obtenu ne correspond pas à l’attendu
    • Quand c’est possible, il faut utiliser un alias unique ou intégrer directement l’expression calculée dans le ORDER BY de la fonction de fenêtre
  • Indiquer à quelle table appartient chaque colonne

    • Dans une requête complexe avec plusieurs jointures, il faut pouvoir remonter un problème de valeur jusqu’à la table source
    • Si deux tables partagent le même nom de colonne et qu’on ne précise pas son origine, le SGBDR peut lever une erreur
    • L’exemple utilise des alias de table comme vc.video_id et metadata.season pour rendre la provenance des colonnes explicite

Ordre d’exécution, documentation et noms de sauvegarde

  • Comprendre l’ordre d’exécution du SQL

    • Parmi les conseils les plus importants pour apprendre le SQL figure la compréhension de l’ordre d’exécution des clauses
    • Connaître cet ordre peut profondément changer la manière d’écrire des requêtes
    • Comme ressource, l’article propose A beginner’s guide to the true order of SQL operations
  • Lire la documentation jusqu’au bout

    • Un cas est cité où GREATEST() a été utilisé sur Snowflake pour renvoyer la date la plus récente parmi plusieurs colonnes de date
    • GREATEST() renvoie NULL si l’un de ses arguments est NULL
    • En lisant davantage la documentation, on aurait pu utiliser GREATEST_IGNORE_NULLS() au lieu de COALESCE(GREATEST(...), ...)
    • Dans bien des cas, parcourir la documentation prend moins d’une minute et peut éviter de longues recherches sur un comportement inattendu
  • Donner des noms explicites aux requêtes enregistrées

    • Pour éviter de ne pas retrouver une requête qu’il faudra relancer ou consulter plus tard, mieux vaut l’enregistrer sous un nom explicite
    • Le nom contient généralement le sujet de la requête, le mois d’exécution et le nom du demandeur
    • L’exemple suit le format Lapsed users analysis - 2023-09-01 - Olivia Roberts

2 commentaires

 
hiyama 2024-09-26

Les virgules placées en tête dans ce billet ont toutes été écrites ici comme des virgules finales. Dans l’original, elles sont saisies en tête.

-- Good:  
SELECT   
timeslot_date  
, timeslot_channel   
, overnight_fta_share  
, IFF(DATEDIFF(DAY, timeslot_date, CURRENT_DATE()) > 7, -- First argument of IFF.  
	LAG(overnight_fta_share, 1) OVER (PARTITION BY timeslot_date, timeslot_channel ORDER BY timeslot_activity), -- Second argument of IFF.  
		NULL) AS C7_fta_share -- Third argument of IFF.  
, IFF(DATEDIFF(DAY, timeslot_date, CURRENT_DATE()) >= 29,   
		LAG(overnight_fta_share, 2) OVER (PARTITION BY timeslot_date, timeslot_channel ORDER BY timeslot_activity),   
			NULL) AS C28_fta_share  
FROM timeslot_data  
;  
 
GN⁺ 2024-09-26
Avis sur Hacker News
  • Le conseil que j’ajouterais est le suivant : apprenez vraiment votre serveur de DB et vérifiez souvent les plans d’exécution. On peut obtenir des résultats inattendus, donc il vaut mieux ajuster puis revérifier
    En général, EXISTS est plus rapide que IN, et NOT EXISTS se comporte différemment de EXCEPT dans la gestion de NULL. Plutôt que de joindre des tables puis de filtrer les lignes avec quelque chose comme DISTINCT, utiliser des colonnes de sous-requête dans la liste SELECT peut être beaucoup plus rapide. Cela peut rester vrai même si vous récupérez plus de 10 valeurs depuis la même table, et même si le serveur de DB prend en charge les lateral joins. En revanche, la sous-requête ne doit renvoyer au maximum qu’une seule ligne
    Les requêtes qui ne sont pas ponctuelles ne devraient pas faire de scan complet de table. Le scan de table d’aujourd’hui peut devenir la panne de demain, donc il faut ajouter des index. Il faut aussi garder en tête que la clause GROUP BY détermine souvent l’utilisation des index
    Si vous devez filtrer sur une expression, par exemple vérifier qu’une sous-chaîne est égale à une certaine valeur, vous pouvez ajouter une colonne calculée et l’indexer. Certaines DB prennent directement en charge les index sur expression. Utiliser UNION ALL au lieu de OR rend souvent les choses beaucoup plus rapides, même avec des requêtes complexes ou de nombreuses conditions OR
    Quand la DB ne choisit pas intelligemment l’ordre de filtrage, il peut aussi être utile de JOIN une sous-requête pour forcer cet ordre

    • Le plus utile, c’est d’apprendre le DBMS lui-même. Chaque DB a ses particularités en matière de performances et de niveaux d’isolation, ainsi que des fonctionnalités bonus différentes ; on ne peut donc pas y échapper
      Une chose qui m’a semblé intéressante dans Postgres — et il est possible que d’autres DB fassent pareil —, c’est que si l’on partitionne manuellement une opération INSERT (SELECT ...) en fonction du nombre de cœurs CPU, on peut obtenir une accélération presque linéaire. C’était possible même avec une dizaine de joins. Il faut d’abord regarder EXPLAIN, trouver le join le plus interne ou le plus externe, puis exécuter des requêtes parallèles séparées pour chaque plage de lignes (id >= start AND id < end). Dans un travail il y a 6 ans, on a beaucoup utilisé cette méthode pour une raison bizarre. Postgres 10+ a ajouté du parallélisme, mais à ma connaissance ce n’est pas encore aussi avancé que ça
    • Je ne comprends pas exactement ce que veut dire utiliser des « colonnes » de sous-requête dans la liste SELECT
      Par exemple, si j’exécute SELECT column1, (SELECT column2, column3, ... FROM table_b WHERE table_a.id = table_b.a_id) FROM table_a, j’obtiens comme prévu « subquery must return only one column ». Est-ce que cela signifie qu’il faut renvoyer plusieurs colonnes sous forme de record/type composite ?
      Je ne voyais pas immédiatement pourquoi la clause GROUP BY détermine souvent l’utilisation des index ; pour ceux que ça intéresse, cet article l’explique bien étape par étape : https://www.brentozar.com/archive/2015/06/indexing-for-group...
    • D’accord. Il faut utiliser EXPLAIN et apprendre à l’interpréter avec l’outil que l’on préfère. Il faut aussi monitorer les requêtes
      Dans ma précédente startup, on avait installé PgHero, et ça nous a énormément aidés pour l’optimisation des performances et la priorisation
    • Même des requêtes bien conçues se comportent souvent autrement que prévu. Les cas typiques sont des statistiques de colonnes qui n’ont pas été mises à jour, ou des données fragmentées dans une grosse table. Par exemple des insertions de clés primaires aléatoires
    • Je ne suis pas d’accord avec « les requêtes qui ne sont pas ponctuelles ne doivent pas faire de scan complet de table. Le scan de table d’aujourd’hui peut devenir la panne de demain »
      Il existe des requêtes pour lesquelles le scan complet de table est la stratégie d’accès la plus efficace. C’est généralement le cas des requêtes analytiques/d’agrégation qui lisent toute la table, et parfois même lorsqu’on ne récupère que 50 % des lignes, un scan de table est préférable.
      Je ne vois pas non plus très bien comment un scan de table en lecture seule peut provoquer une panne. Il ne bloque pas les accès concurrents. Son seul inconvénient est de générer une forte charge d’I/O ; mais si le serveur n’est pas capable d’encaisser ça, il était dès le départ gravement sous-dimensionné
  • Les 3 exemples de la section « lisibilité » sont étranges. Les 2 premiers sacrifient littéralement la lisibilité pour faciliter l’écriture, et le dernier est une sorte de monstre illisible que même l’indentation ne sauve presque pas

    • Le style avec virgules en début de ligne a des avantages au-delà de la lisibilité. Par exemple, dans un système de gestion de versions, le format une ligne par argument + virgule en début de ligne fait qu’un changement d’argument n’apparaît que comme un diff d’une seule ligne
      Je pense que les développeurs regardent presque autant l’historique des commits que le code source lui-même
    • Je ne suis pas particulièrement fan de l’apparence des deux premières conventions, mais ce sont des conventions réellement utilisées par des gens qui écrivent réellement du SQL. Je comprends aussi pourquoi elles existent
      Je les ai vues assez souvent pour qu’elles ne me gênent plus vraiment
    • Une autre option consiste à écrire du SQL n’importe comment, comme un enfant de trois ans qui découvre MSPaint pour la première fois, puis à appuyer sur le bouton « beautifier » et à partir déjeuner tôt
    • Je ne vois pas pourquoi vous trouvez ça pire
      Je ne vois pas de problème non plus
      Et ça ne me semble pas incorrect
    • Qui diable met les colonnes du bloc SELECT sur des lignes séparées tout en laissant des lignes de 150 caractères telles quelles ? C’est une définition détraquée de la lisibilité. Et je ne vais même pas commencer sur l’histoire des virgules
      En revue de code, personne ne lit correctement les longues lignes. C’était le plus gros problème d’AngularJS. Des merges étaient mal résolus et tout cassait, parce qu’autour de la 90e colonne les yeux commencent à décrocher. J’ai connu plus d’une demi-douzaine d’équipes avec revue de code, et c’était toujours pareil. Même en étant très conscient du problème et en essayant de l’éviter, je fais encore moi-même des erreurs environ moitié moins souvent que les autres
      Coupez un peu vos lignes. Surtout si vous montrez des exemples à d’autres personnes
  • Voici quelques conseils pour travailler avec des procédures stockées complexes

    1. Au début de la procédure, copiez immédiatement les tables permanentes dans des tables temporaires, en ne sélectionnant/limitant/filtrant que les lignes nécessaires
    2. Au milieu, manipulez les tables temporaires selon les besoins
    3. À la fin, mettez à jour les tables permanentes dans une transaction. Si une erreur est détectée, annulez immédiatement la transaction et terminez la procédure. En suivant ces trois étapes, on obtient une meilleure concurrence, et l’on peut relancer la procédure sans avoir à nettoyer manuellement les résidus de données
    4. Il faut être extrêmement prudent avec les tables distantes. Comme elles ne se trouvent pas dans le RDBMS courant, il est très probable que vous ne puissiez presque pas exploiter les statistiques ou les index de ce RDBMS. Dans beaucoup de cas, il est plus rapide de dumper/copier toute la table distante dans une table temporaire avant de travailler dessus. Le maximum que l’on puisse attendre d’une table distante est à peu près l’exécution d’une clause WHERE. Si vous tentez un JOIN ou une opération complexe, vous risquez fortement un timeout
    5. Les plans d’exécution peuvent facilement induire en erreur. Dans certains cas, le plan d’exécution peut retomber sur un traitement ligne par ligne et les performances se figent. Il vaut souvent mieux découper une procédure stockée complexe en petites étapes utilisant des tables temporaires
    6. Pour voir ce que fait réellement le RDBMS, il faut toujours vérifier le plan d’exécution
    • Il m’est arrivé d’améliorer fortement les performances d’une requête en annulant du code où le point 5 avait été appliqué alors que ce n’était pas vraiment nécessaire. Parfois, découper une requête en plusieurs petites requêtes est bien moins efficace que de donner toute la requête à l’optimiseur de requêtes et de le laisser trouver le meilleur chemin
      Si vous appliquez le point 5 sans le point 6, vous risquez fort de ne pas voir que vous faites quelque chose de sous-optimal. Mon conseil est d’éviter l’optimisation prématurée, d’écrire d’abord de la façon la plus intuitive, puis de n’optimiser que lorsque c’est nécessaire. Le plus important est de ne pas écrire du SQL de manière procédurale. Il s’agit de décrire les données que l’on veut, pas d’ordonner au moteur comment aller les chercher
    • Je n’aime pas devoir utiliser des tas de tables temporaires, mais je tombe souvent sur des requêtes qui ne se termineraient jamais si on les laissait au planificateur de requêtes. Comme pour les compilateurs, on surestime largement les capacités des query planners
      À l’inverse, Microsoft continue d’ajouter des avertissements disant en gros de ne pas essayer de le tuner, comme si le query planner savait toujours mieux que tout le monde
    • Ces règles peuvent être parfaitement valables pour la base de données d’un fournisseur donné, mais d’autres bases peuvent avoir des priorités, des caractéristiques et des compromis très différents
      La version de la base de données peut aussi jouer
    • Les points 1 à 3 sont corrects si l’on peut garantir que la taille des données reste raisonnable. Mais si les données deviennent trop volumineuses pour le matériel, copier un gros dataset puis remettre à jour un gros dataset peut ajouter un overhead considérable
  • Je n’aime pas le développement « au cas où ». Cela vaut pour les interfaces, et aussi pour les placeholders comme where 1=1
    Faites-le quand c’est nécessaire. Ne le faites pas sous prétexte qu’un jour, peut-être, cela pourrait servir. Le code de production n’est pas l’endroit où laisser des aides au développement. Pendant le développement, faites ce que vous voulez, mais dans le code de production, la lisibilité et une intention claire sont bien plus importantes

    • Est-ce que vous qualifiez entièrement toutes les références aux noms de tables et de colonnes ? J’ai souvent vu cela améliorer la lisibilité d’au moins un ordre de grandeur, mais cela devient vite extrêmement verbeux et incroyablement pénible à écrire
  • Encore un point sur les « anti-jointures ». Si vous voulez seulement vérifier l’existence de lignes correspondant à une condition dans une autre grande table ou une sous-requête, mieux vaut utiliser EXISTS plutôt que IN ou LEFT JOIN
    EXISTS renvoie vrai dès qu’il trouve une correspondance. Avec LEFT JOIN et IN, le moteur rassemble tous les résultats avant l’évaluation

    • Ce point m’a un peu dérouté. Dans tous les cas que j’ai testés, (NOT) EXISTS produisait un meilleur plan d’exécution que (LEFT) JOIN ou (NOT) IN, ou bien le même plan
      Et en plus, l’intention est plus claire
  • À propos de « commentez votre code », au moins avec MSSQL, on recommande souvent d’utiliser /**/ plutôt que -- pour les commentaires. Des fonctionnalités comme Query Store stockent souvent les requêtes sans retours à la ligne ; quand on récupère une requête depuis là, il faut tout corriger à la main au lieu de pouvoir simplement utiliser le formatter de l’IDE

    • Ça ressemble à un bug de Query Store
    • Peut-on caster en XML ? Je m’en sers pour OBJECT_DEFINITION
      select name,cast((select OBJECT_DEFINITION(object_id) for xml path('')) as xml) from sys.procedures
      Comme les retours à la ligne sont conservés, cela peut être plus facile à nettoyer. En revanche, d’autres caractères XML sont altérés, par exemple > devient >. Une autre option consiste à utiliser VARBINARY et quelque chose qui le décode ensuite
  • Tout le monde s’emballe sur la suggestion des virgules, mais le 1=1 dans la clause WHERE, c’est censé être une bonne idée ? Quand je vois ça en code review, je ne sais pas quoi penser de l’auteur

    • On peut le justifier pour la même raison que les virgules finales : modifier l’instruction WHERE n’affecte pas les autres lignes, ce qui facilite la code review
      Mais si la raison est d’ajouter des conditions dynamiques, comme ici, là où je travaille, ce serait clairement un motif de licenciement
  • Quelqu’un pourrait-il partager des lignes directrices générales sur l’endroit où tracer la limite entre accélérer les choses via la configuration de la DB — une approche qui relève presque de « l’achat » — et une « construction » qui revient en pratique à l’implémenter manuellement ? D’après mon expérience limitée, les DBA compétents sont bien mieux payés et travaillent ailleurs, donc cette tâche retombe souvent sur les développeurs d’applications. Comme dit plus haut, il est important de connaître les DB.
    Un exemple typique est celui de données qui s’accumulent massivement avec le temps, et dont les plus récentes sont consultées le plus souvent. Un DBA peut maintenir des accès rapides avec du partitionnement ou des index partiels, mais un développeur d’applications peut aussi déplacer en arrière-plan les enregistrements vers une table d’archives séparée, tout en continuant à prendre en charge des fonctionnalités comme la recherche finale sur l’ensemble du jeu de données. J’ai aussi l’impression que, lorsque l’on est limité par exemple par le manque de fonctionnalités d’une DB cloud, des outils pourraient assez largement automatiser le travail initial qui consiste à scinder une table en plusieurs au bon moment.
    Un autre choix d’administration consiste à stocker tous les gros blobs/fichiers dans une base de données séparée, ou dans le système de fichiers, afin d’utiliser une configuration de stockage différente. Là encore, cela peut être pris en charge par la DB ou géré manuellement.
    À l’extrême, on pourrait même aller jusqu’à implémenter soi-même des index. Par exemple, avoir une énorme table avec une clé primaire auto-incrémentée et de nombreuses colonnes, puis créer une table séparée contenant cet ID et quelques colonnes recherchables. On pourrait même aller jusqu’à la recherche plein texte ou aux vecteurs.
    Pour implémenter manuellement le patron de vue matérialisée dans MSSQL 2016+, une astuce utile consiste à l’associer au basculement de partitions. C’est bien expliqué et implémenté dans https://github.com/cajuncoding/SqlBulkHelpers?tab=readme-ov-.... C’est une petite bibliothèque que j’ai trouvée par hasard, commercialement la plus utile pour moi, mais peu visible dans les résultats de recherche et avec peu d’étoiles ; elle se concentre sur les insertions en masse dans MSSQL depuis .NET. Je pense que l’automatisation du basculement de partitions est un bon exemple d’une limite bien placée entre achat et construction.

  • Élément manquant : il faut arrêter d’utiliser SELECT *. Il est presque certain que vous n’avez pas besoin de toute la largeur de la table ; cela augmente les données à filtrer et à transférer, et empêche aussi une fonctionnalité élégante : les semi-jointures.

    • Les personnes qui utilisent SQL se répartissent globalement en deux catégories : les analystes et les développeurs.
      Si vous êtes développeur, oui. SELECT * comporte des pièges, et il faut presque toujours expliciter les colonnes, ou utiliser un query builder qui le fait à votre place.
      Mais si vous êtes analyste, la vie est courte, et parfois vous n’avez pas envie de taper toutes les colonnes une par une. SELECT * est acceptable.
  • C’est peut-être un peu hors sujet, mais est-ce une pratique acceptable pour un mainteneur de fermer simplement des pull requests sans aucun commentaire ni discussion ?
    Je pose la question en tant que personne ayant parfois contribué, ou essayé de contribuer, au dépôt.
    Exemple : https://github.com/ben-n93/SQL-tips-and-tricks/pulls?q=is%3A...